Count how many occurrence of a value in table

The full traceback would provide you with that information. It’s one of the reasons why I usually ask for that full traceback and not just the error message.

However, having said that, your conjecture is correct. It’s from this line:

Think for a moment about what this line is trying to do.

So, looking at the error message itself, what’s it telling us?

In parts:

django.core.exceptions.FieldError:

The name of the error.

Cannot resolve keyword 'project_id' into field.

What are the keywords in a function call? See the definition for argument at Glossary — Python 3.10.1 documentation.

What can you do about it? The error gives you your options.

I tried changing to project_name_id. but it throws this error:

Cannot resolve keyword 'project_id' into field. Choices are: answer, id, project_name, project_name_id, questionnaire, questionnaire_id, user

def get_questionnaire(request, project_name_id, questionnaire_id):

    # Get the Response object for the parameters
    response = Response.objects.get(
        project_id=project_name_id, questionnaire_id=questionnaire_id
    )
  path('questions/<int:project_name_id>/<int:questionnaire_id>', view=get_questionnaire, name="questions"),

and the url:

<a class="dropdown-item" href="{% url 'questions' project.name.id item.id %}?next={{ request.path|urlencode }}">{{ item.title }}</a>

Please read the referenced docs:

Or, if you want to look at it a different way, what is each component of this statement?

    response = Response.objects.get(
        project_id=project_id, 
        questionnaire_id=questionnaire_id
    )

Hmmm. Im not really sure what to say about this.

response = Response.objects.get( project_id=project_id, questionnaire_id=questionnaire_id )

This is getting the objects in the Response model where the project_id matches what has been past in the original request and the same for the questionnaire both of these are FKs to Project and Questionnaire

Project_id doesn’t exists as a field in Response, but project_name does so i assumed i needed to pass project_name_id?

So i changed my url to be project_name_id and the view to project_name_id` is that not correct?

I’ve read the link, but i can’t piece it together.

Ok, lets break this down:

Starting from this statement:

What is:

  • response
  • Response
  • objects
  • get
  • project_id (before the =)
  • project_id (after the =)
  • questionnaire_id (before the =)
  • questionnaire_id (after the =)

You should be able to identify what each one of those components of the statement are and what they do relative to the statement as a whole.

I’ll help get you started with the first one:

  • response
    It’s a variable that will contain a reference to an instance of a Response object.

Response is the model with a FK to Project and Questionnaire which is acting an a intermediate table between the answer, project and questionnaire?

Objects is all the items in the model(s)

Get is Getting a specific object from objects?

project_id = a variable
= project_id is the project_id being passed by the request

same for questionnnarie both before and after =

So what is that telling me…:thinking:

Absolutely yes.

Not quite, objects is a manager. It is a class used to access a model by a query. (Not critical at this point to understand the difference, but important enough to know.

Close enough for now. Get is a query to return a single instance of an object. (The key distinction here is that it’s not “getting” anything from objects. It’s an attribute (actually a method) on the objects class.

And this is where we need to clear things up a little. It’s not a variable, it’s a reference to a field.
See Making queries, and in particular, the sections Retrieving objects and in this case, field lookups

This is one of those key topics that you’re going to need to be absolutely solid on.

Ah right so the project_id field doesn’t exist in Response? and therefore project_id should be
project_name_id? As project name exists as a field in the Response model

or not?

I’ve replaced the line in the view with
response = Response.objects.get(project_name_id=project_id, questionnaire_id=questionnaire_id)

This now throws another error. matching query does not exist which is because there are no entries in Response for that project_id

If I manually create a response entry for the project the template loads :slight_smile:

I assume you left this bit out intentionally for me to work out :slight_smile: or is this not working as expected?

Would it be that i need to change the query to a If doesn't exist then carry on?

    if Response.objects.get(project_name_id=project_id, questionnaire_id=questionnaire_id).exists():
        response = Response.objects.get(
        project_name_id=project_id, questionnaire_id=questionnaire_id
    )
    else:
        response = "null"

The above doesn’t work

No, you do need to create the responses. Creating them could occur in a couple different places, and is a business-logic decision.

Keep in mind that Response is a many-to-many through table between Questionnaire and Project. It’s what identifies the association of a Questionnaire to a Project.

My current impression is that every questionnaire is to be answered for every project.

If that assumption is true, then you might want to add code to create the appropriate Response objects when you either create a new Questionnaire or when a new Project is added.

If that assumption is not correct, then you’ll need some other facility to assign Questionnaires to Projects.

Now, if you wish to defer that creation until this view, that’s OK too - see the get_or_create method. This would let you retrieve the Response if it exists, or create a new one if necessary.

I like the idea of creating the response when the project is created. Cause like you said every project will need every questionnaire answered. But, I don’t want that to be a requirement to add a project.

So you could create the project and then come back to the questionnaires.

At the moment the questionnaire and project are required in the Response So thinking about it, would it make more sense to create the response when you first go from project to a questionnaire?

So go to questionnaire 1 would create a response object for the project and questionnaire 1? This would mean I create the response entry with this view.

Also, Ken. Can you confirm my thinking is correct for the project_id = In this case the project_id has to be a field in the model for the model being called i.e., Response And basically what is being asked is that the value of the query will be added to the project_id field in Response?

But because project_id doesn’t exist as a field we use the project_name_id as this is the FK?

And lastly, how do I style the ModelFormset? I’ve been doing this with

class AnswersFormset(ModelForm):
    class Meta:
        model = Answer
        fields = ('answer',)

        widgets = {
            'answer': forms.Select(attrs={'class': 'form-select'}),
        }

but it doesn’t seem to be applied to this form?

many thanks

Having a Response object does not mean that the Questionnaire needs to be answered at that time. You can create the Project, and then create the Response objects to associate that new Project with every existing Questionnaire.

Again, that’s a business-logic decision. It doesn’t physically matter either way. It’s 100% a judgement call.

Assuming this is a reference to the Response get query from earlier. The left side of that expression (project_id=project_id, ... is the name of the field to be tested.
This is a query - a question.
You’re making a request of the database.

(I’m a little confused by your wording here:

I don’t know what you’re trying to say with the phrase value of the query will be added to. So I’m going to bypass this for the moment and approach this from a different direction.)

Let me rephrase this slightly to hopefully clear up some of the confusion.

Assume now that the view is defined as:

    def get_questionnaire(request, project_parameter, questionnaire_parameter):

I’m changing the names of the parameters for clarity.

The query would now be:

response = Response.objects.get(
        project_id=project_parameter, questionnaire_id=questionnaire_parameter
    )

Which means you’re asking this question:

“Mr Database, please find the Response object for me where the value in the field named project_id is equal to the value of project_parameter and where the value in the field named questionnaire_id is equal to the value of `questionnaire_parameter.
If you don’t find exactly 1 of these, tell me that you can’t answer my question.”

Based on the above and the previously referenced docs on queries, what do you think at this point?

A formset (of any flavor, model, inline, or regular) is not a form. Its a container for a list of forms. See Formsets | Django documentation | Django.
You typically don’t style a formset - you style the form being used by the formset. (Or, to perhaps be more precise, styling a formset is fundamentally different than styling a form.) They’re two different objects. A formset is not a form.

See:

I think so because project_id only exists as the FK? No?

I’ve now rendered the forms and will work on creating the Response and the POST :slight_smile:

Obviously, I have more questions…

def get_questionnaire(request, project_id, questionnaire_id):

    # Get the Response object for the parameters
    response = Response.objects.get(
        project_name_id=project_id, questionnaire_id=questionnaire_id
    )

    AnswerFormSet = modelformset_factory(Answer, form=AnswerForm, fields=('answer','notes',), extra=0)

    answer_queryset = Answer.objects.filter(response=response
    ).order_by('question__sequence'
    ).select_related('question')

    if request.method == 'POST':
        # Left as an exercise for the reader
        pass
    else:
        # Get the list of questions for which no Answer exists
        new_answers = Question.objects.filter(
            questionnaire__response=response
        ).exclude(
            answer__response=response
        )

        # This is safe to execute every time. If all answers exist, nothing happens
        for new_answer in new_answers:
            Answer(question=new_answer, response=response).save()

        answer_formset = AnswerFormSet(queryset=answer_queryset)

    return render(request, 'pages/add_critical_fundamentals.html', {'formset': answer_formset})

To style the form i am passing in AnswerForm into AnswerFormSet = modelformset_factory(Answer, form=AnswerForm, fields=('answer','notes',), extra=0)

This is working, but is it the correct way?

Using the view above I can use this for all questionnaires right? I would just need to render the correct html template.

I was thinking I could do something like `pages/some_variable.html?

The variable would need to get the questionnnaire_title value? Is this possible from the response model via something like response.questionnaire.title ?

What is the correct way to show data from different models that are linked, I need to get the project_name from the Project model.

{{ form.instance.response.project.project_name }}

It is one correct way. (Rarely is there only one right way to do something. It’s a correct way to do it, it’s not the correct way.)

The intent is that you can use this for all questionnaires. You shouldn’t even need to change the template - if you need to make differences between questionnaires, you can encapsulate those differences in a couple different ways.

I wouldn’t recommend that. What specifically needs to change between questionnaires? Just the title?

It’s not clear where or how you’re trying to use this. This may or may not be right.

These are Django templates you’re working with. If you need a title on the page outside the formset, you can pass it in to the template through the context.

return render(request, 'pages/add_critical_fundamentals.html', 
    {'formset': answer_formset, 'title': response.project_name.title })

(I’m not seeing a definition for the Project class, so I don’t know what field might contain the title.)

Anyway, you can then render the title in the template as {{ title }}

Thanks, Ken. I realise i can just change the title of the template with {{ title }} so this avoids have to create multiple templates :slight_smile:

Ideally though if the form has been partially completed, I like to change the title to update. I think I can do this with an If statement.

Now on to the last bit I think. I am pretty sure you know was coming. The POST

if request.method == 'POST':
        form = AnswerForm(request.POST)
        answers = form.save(commit=False)
        answers.answer = response
        answers.response = response      
        answers.save()
        return HttpResponseRedirect(next)

This is throwing an error

Internal Server Error: /questions/1/1
Traceback (most recent call last):
  File "d:\AzDoProjects\ValhallaLondon\venv\lib\site-packages\django\db\backends\utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
psycopg2.errors.NotNullViolation: null value in column "question_id" violates not-null constraint
DETAIL:  Failing row contains (127, Response object (2), null, 2, ).


The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "d:\AzDoProjects\ValhallaLondon\venv\lib\site-packages\django\core\handlers\exception.py", line 47, in inner
    response = get_response(request)
  File "d:\AzDoProjects\ValhallaLondon\venv\lib\site-packages\django\core\handlers\base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "D:\AzDoProjects\ValhallaLondon\valhalla_WebApp\apps\views.py", line 59, in get_questionnaire
    answers.save()
  File "d:\AzDoProjects\ValhallaLondon\venv\lib\site-packages\django\db\models\base.py", line 726, in save
    self.save_base(using=using, force_insert=force_insert,
  File "d:\AzDoProjects\ValhallaLondon\venv\lib\site-packages\django\db\models\base.py", line 763, in save_base
    updated = self._save_table(
  File "d:\AzDoProjects\ValhallaLondon\venv\lib\site-packages\django\db\models\base.py", line 868, in _save_table
    results = self._do_insert(cls._base_manager, using, fields, returning_fields, raw)
  File "d:\AzDoProjects\ValhallaLondon\venv\lib\site-packages\django\db\models\base.py", line 906, in _do_insert
    return manager._insert(
  File "d:\AzDoProjects\ValhallaLondon\venv\lib\site-packages\django\db\models\manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "d:\AzDoProjects\ValhallaLondon\venv\lib\site-packages\django\db\models\query.py", line 1270, in _insert
    return query.get_compiler(using=using).execute_sql(returning_fields)
  File "d:\AzDoProjects\ValhallaLondon\venv\lib\site-packages\django\db\models\sql\compiler.py", line 1416, in execute_sql
    cursor.execute(sql, params)
  File "d:\AzDoProjects\ValhallaLondon\venv\lib\site-packages\django\db\backends\utils.py", line 98, in execute
    return super().execute(sql, params)
  File "d:\AzDoProjects\ValhallaLondon\venv\lib\site-packages\django\db\backends\utils.py", line 66, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "d:\AzDoProjects\ValhallaLondon\venv\lib\site-packages\django\db\backends\utils.py", line 75, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "d:\AzDoProjects\ValhallaLondon\venv\lib\site-packages\django\db\backends\utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "d:\AzDoProjects\ValhallaLondon\venv\lib\site-packages\django\db\utils.py", line 90, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "d:\AzDoProjects\ValhallaLondon\venv\lib\site-packages\django\db\backends\utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
django.db.utils.IntegrityError: null value in column "question_id" violates not-null constraint
DETAIL:  Failing row contains (127, Response object (2), null, 2, ).

[22/Dec/2021 09:48:14] "POST /questions/1/1?next=/show_project_details/1 HTTP/1.1" 500 157218

Looking at the database I can see that some values are being added.

The Question and Response values are there, but the Answer field is not.

So I’m playing around with this, but not really understanding

answers.answer = response
answers.response = response      
answers.save()

You are POSTing a formset, not a form.

See Creating forms from models | Django documentation | Django

Understood. That’s sorted :slight_smile: BUT now my redirect is broken :frowning:

TypeError: quote_from_bytes() expected bytes

It this line return HttpResponseRedirect(next) which is meant to return me back to the project details page. It works for everything except this now.

What is “next”?

What are you supposed to supply to HttpResponseRedirect as a parameter?

This is built form these:
<input type="hidden" name="next" value="{{ request.GET.next }}">
{% url 'questions' project.id item.id %}?next={{ request.path|urlencode }}

But at that point in the code, what is the value of “next”?