Small query regarding template variable lookup order

Hi! I may have found either a small mistake in the official Polls tutorial or a flaw in my understanding

This concerns the part 3 of the tutorial at Writing your first Django app, part 3 | Django documentation | Django
This part is explaining how the Django template lookup works. It states:-

The Polls tutorial implies that the template dot syntax lookup order is 1. Instance dict 2. Dynamic attribute lookup 3. List index lookup & so on

The Polls tutorial also explicitly states that, in the case of question.question_text
the 2. Dynamic attribute lookup succeeds @line 373
Thereby, implying that the 1. instance dict lookup failed
Therefore, I am expecting question.__dict__['question_text'] to fail

But I found by setting a breakpoint in the template using Pycharm that question.dict was:

{
‘_state’: <django.db.models.base.ModelState at 0x7f12fc7bea60>,
‘id’: 1,
‘question_text’: “What’s up?”,
‘pub_date’: datetime.datetime(2021, 12, 18, 15, 45, 52, tzinfo=)
}

This means that the dictionary lookup on the object question i.e. ‘1. instance dict lookup’ does NOT fail

Am I mistaken in my understanding somehow?

Does it have anything to do with deferred loading of

Question.question_text()

Minor edit to the above question:-
I meant to write question.__dict__ instead of question.dict

Breaking this down a little:

first Django does a dictionary lookup on the object question.

This is not making a reference to an attribute lookup. It’s saying that it’s first going to try to use question as a dict - question['question_text']. It’s not saying that it’s performing the attribute lookup, that’s the second step:

Failing that, it tries an attribute lookup

(Keep in mind that when question is an object, question.__dict__['question_text'] is question.question_text, so using your interpretation would mean that it would be doing the same thing twice.)

So no, the implication is not what you’re listing. The implication is (1) Dictionary lookup then (2) Attribute lookup, etc.

Merry Christmas Ken!
Thanks a lot for taking a look at my question.

I disagree with this conclusion:-

I have prepared a twisted example just to show that
question.__dict__['question_text'] is not always question.question_text

My twisted example:-

class TwistedQuestion:
    """
    This is a convoluted example, just to showcase a point.
    This does not represent a Django Model Class in any shape or form :)
    """
    def __getattr__(self, name):
        if name == "question_text":
            return "How are you? (Note: got through attribute lookup)"
        raise AttributeError(f'TwistedQuestion class has no attribute {name}')

question = TwistedQuestion()
print(question.__dict__)               # => {}
print(question.__dict__['question_text'])     # raises KeyError
print(question.question_text)          # => 'How are you? (Note: got through attribute lookup)'

Let me restate my earlier question more succintly:-

Context:-
Question is a Django Model defined as:-

class Question(models.Model):
    question_text = models.CharField(max_length=200)

question is an instance of the above Django Model

In the Polls tutorial, we are rendering the question_text into a django template.
The template looks like this:-

<li>{{ question.question_text }}</li>

The Django docs is explaining how the above template’s dot-syntax works.

The Django docs states:-

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, which fails in this case (implied)

Failing that, it tries an attribute lookup – which works, in this case.
If even attribute lookup had failed, it would’ve tried a list-index lookup.

But I find that the dictionary lookup on the object does not fail in the above case when I run my code. Hence my question.

Yes, a dictionary lookup fails. It’s looking for question['question_text'], not question.__dict__['question_text']. That’s the answer to your question. (Unless you’re saying that question[‘question_text’] is working, which is not my understanding from your example.)

Now, your contrived example aside (which is quite cute by the way, thanks!), quoting directly from the Python docs at 3. Data model — Python 3.12.0 documentation

Custom classes
Custom class types are typically created by class definitions (see section Class definitions). A class has a namespace implemented by a dictionary object. Class attribute references are translated to lookups in this dictionary, e.g., C.x is translated to C.__dict__["x"] (although there are a number of hooks which allow for other means of locating attributes).

So yes, I was making reference to the common case in my statement, without explicitly acknowledging the exceptions.

1 Like

I realize my misunderstanding now.
The dictionary lookup means question["question_text"]

It was wrong of me to interpret it as question.__dict__["question_text"]

Thanks a lot! Ken

In case someone wants to try out the lookup order in action,
pass an instance of this TwistedQuestion_v2 into the template context:-

class TwistedQuestion_v2:
    def __getattr__(self, name):
        "This method is a fallback"
        if name == "question_text":
            return "How are you? (Note: got through __getattr__)"
        raise AttributeError(f'Question class has no attribute {name}')
    def __getitem__(self, name):
        if name == "question_text":
            return "Are you not entertained!? (Note: got through __getitem__)"

Then, during rendering of templates, it will first resolve to question["question_text"] which happens to be 'Are you not entertained?...'