Django tutorial part 3 - question_id vs question.id

160 views
Skip to first unread message

Acetone

unread,
Mar 26, 2016, 1:36:49 PM3/26/16
to Django users
Hello,

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?"

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?

Hmm, I think I might have answered my own question, but I definitely thought I had exhausted my resources before deciding to write this post. ; )

Thank you,

Kurt



jorr...@gmail.com

unread,
Mar 26, 2016, 2:04:13 PM3/26/16
to Django users
I believe Django lets you access the pk field of a table (which is created automatically unless you define it explicitly on your model) in multiple ways: self.pk, self.id, and self.<modelname>_id. Someone correct me if I'm wrong.

Acetone

unread,
Mar 27, 2016, 2:47:10 AM3/27/16
to Django users
Cool, I can try those to see how it works.  Thank you.

James Schneider

unread,
Mar 27, 2016, 7:35:33 AM3/27/16
to django...@googlegroups.com
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

Acetone

unread,
Apr 7, 2016, 4:25:41 AM4/7/16
to Django users
This is awesome, and I appreciate the time you spent writing it.  Besides just answering the question I had, this also helped me make some new connections around OOO topics.

Also, I do not intend to use the question id as a numbering system - I was just trying to access the id for its own sake and for figuring things out.  The only thing I use ids for in my own database is linking different tables together.

Thank you,

Kurt

Peter of the Norse

unread,
Apr 16, 2016, 4:15:00 PM4/16/16
to django...@googlegroups.com
On Mar 26, 2016, at 8:04 AM, jorr...@gmail.com wrote:
>
> I believe Django lets you access the pk field of a table (which is created automatically unless you define it explicitly on your model) in multiple ways: self.pk, self.id, and self.<modelname>_id. Someone correct me if I’m wrong.

You can’t use <modelname>_id. The closest is the foreign key ID.

Peter of the Norse
Rahm...@Radio1190.org



Reply all
Reply to author
Forward
0 new messages