Does Django hit the database when I access an object's attributes?

Viewed 1558

Does the following code query the database every time I call question.name?

class Question(models.Model):
    name=models.CharField()
    test=models.ForeignKey(Test)

questions = Question.objects.filter(test=some_test)
for question in questions:
    question_name = question.name

My view takes a set of questions and user responses and scores a worksheet. My goal is to execute all of the scoring without going back to the database, then save the result of my comparisons (a dictionary) using a background task where it's ok if it takes a little longer. I want to eliminate any unnecessary database hits.

1 Answers

Well, your question is unclear but one thing for sure, at least one hit to the database will be required. Indeed, in your code sample your using:

question_name = question.name

But I don't see this working if question is not an instance of Question model beforehand.

So, considering your initial hit on the DB from a query to create an instance, question_name can then be reused as often as you want without having to access the database. See, this as a static instance of a row of your database.

EDIT

Now for your edit, it is not the same behavior. To correctly understand what happens behind the scenes, you need to understand the concept of "Laziness". In fact, since querysets are lazy in Django, this line:

questions = Question.objects.filter(test=some_test)

won't result in any hit on the database. Querysets stands for sets of SQL queries. So, at this point, since you did not ask to fetch something from the database it is not actually evaluated yet. This is the wanted behavior because it makes sure the database is accessed only when it needs to. As the documentation says here are the cases where querysets are evaluated:

Iteration

A QuerySet is iterable, and it executes its database query the first time you iterate over it.

Slicing

Slicing an unevaluated QuerySet usually returns another unevaluated QuerySet, but Django will execute the database query if you use the “step” parameter of slice syntax, and will return a list. Slicing a QuerySet that has been evaluated also returns a list.

Pickling/Caching

repr(). A QuerySet is evaluated when you call repr() on it. This is for convenience in the Python interactive interpreter, so you can immediately see your results when using the API interactively.

len(). A QuerySet is evaluated when you call len() on it. This, as you might expect, returns the length of the result list.

list(). Force evaluation of a QuerySet by calling list() on it

bool(). Testing a QuerySet in a boolean context, such as using bool(), or, and or an if statement will cause the query to be executed

So, as you can see situation 1 apply to your particular case. Iteration will result in hits on the database.

Related