Sunday, March 27, 2016

Re: Django tutorial part 3 - question_id vs question.id

In trying to update my templates after I successfully finished part 3 of the tutorial, I found that I could write something like
<p>You're looking at the results of question {{ question.id }}: "{{ question.question_text }}"</p>
and get: You're looking at the results of question 1: "What's up?"

This worked by accident in your particular situation. The 'id' of the object is a reference to the data row in the database, and is not intended for human consumption in most cases (ie direct display in the template to the end-user). If you created more questions, the ID would increase. You should not rely on it to 'number' your questions, rather number the questions yourself (either manually or via something like django-ordered-model). Using the ID, there's no way to a) renumber questions in a different order or b) group questions together via other attributes such as a 'question group', where you would want the numbering for your questions to start over (presumably).
 

However, if I write
<p>You're looking at the results of question {{ question.question_id }}: "{{ question.question_text }}"</p>
I get: You're looking at the results of question : "What's up?"

I think the answer is that "The template system uses dot-lookup syntax to access variable attributes. In the example of {{ question.question_text }}, first Django does a dictionary lookup on the object question."  And "id" is an attribute of the question object.  But if that's the case, then why is "question_id" used everywhere else (views.py, urls.py)?  Is it because we *define* the question_id parameter by the pattern of the incoming request in views.py?  In other words, if we changed ?P<question_id> to ?P<my_favorite_number>, then we would be writing our views like this:
def detail(request, my_favorite_number):
    question
= get_object_or_404(Question, pk=my_favorite_number)
but still referencing the id as {{ question.id }} in our templates?


"question_id" and question.id are referring to very different things. The reason that {{ question.question_id }} didn't work is because there is no attribute/method/property on "question" called "question_id". The template system will return an empty string in those cases where it can't resolve a value. There is a magic shortcut that might be causing some confusion (explained below).

Within the views, "question_id" is referring to the kwarg that was captured by the URL regex (through the regex capture group of the same name), and is literally just a number (captured as a string). It is used as part of the .filter() or get_object_or_404() call to pull the object with the database ID of whatever the value of "question_id" is (which is coerced in to an integer if it hasn't been already internally). 

You are correct in your assertion about changing the name of the capture group and the view argument and the reflected changes in the get_object_or_404() call. However, "my_favorite_number" is exactly that, just a number. Remember, "question_id" is simply a variable name that lives within the context of your view, that's it.

Now moving on to "question". Once the get_object_or_404() call is made, "question" becomes a fully populated Question object, rather than just a number. You can modify it and run .save() to keep the changes in the database, which you can't do with just "question_id". The "question" in your view is then passed along via the template context as "question". It is possible to change the name of the variable that holds your Question object in the template vs. the view. In fact, this is exactly what the class-based views are doing behind the scenes, and every reference to the primary object of interest (in this case, a Question object) in a template populated by a CBV is called {{ object }} within the template.

Template naming tricks aside, all of the calls within your template to {{ question }} are for an actual Question object. Question objects (per the tutorial models) have no attribute/field called "question_id", hence the reason your template didn't display anything.


To confuse things more, Django does offer some optimization shortcuts for related fields. For example, if you were working with a Choice object in a variable called "choice", you can access the database ID of the related question in two ways (in either a view or a template):

choice.question_id

Both of these calls will result in the same value, the database ID of the Question model that the Choice belongs to. However, there is a big difference in how they get there.

The first is the "standard" way to access information about related objects. The ORM tries to be lazy, so it doesn't try to fetch anything about the related Question unless needed (ie accessing anything choice.question.*). The first time it is called, a database hit is incurred to populate the Question object that is related to the Choice object. Once the Question object is populated, the ID of that object is returned.

The second is less-known, but is used by programmers who are looking to lessen the stress on their database. It takes advantage of a little magic provided by Django that makes the raw ID of a related object available for use without hitting the database a second time (since the FK relationship has the raw ID stored, and it is used to pull the right related object). It uses the format of "foo_id" where "foo" is the name of the relationship.

It is mentioned here in this section of the docs:


HTH,

-James

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/CA%2Be%2BciU6X4e4qzG1YAngCEofZ_bWw0TRVOMJ7aGDN7st8sweiA%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

No comments:

Post a Comment