Django: Can you tell if a related field has been prefetched without fetching it?

Question:

I was wondering if there is a way in Django to tell if a related field, specifically the “many” part of a one-to-many relationship, has been fetched via, say, prefetch_related() without actually fetching it?

So, as an example, let’s say I have these models:

class Question(Model):
  """Class that represents a question."""

class Answer(Model):
  """Class the represents an answer to a question."""
  question = ForeignKey('Question', related_name='answers')

Normally, to get the number of answers for a question, the most efficient way to get this would be to do the following (because the Django docs state that count() is more efficient if you just need a count):

# Note: "question" is an instance of class Question.
answer_count = question.answers.count()

However in some cases the answers may have been fetched via a prefetch_related() call (or some way, such as previously having iterated through the answers). So in situations like that, it would be more efficient to do this (because we’d skip the extra count query):

# Answers were fetched via prefetch_related()
answer_count = len(question.answers.all())

So what I really want to do is something like:

if question.answers_have_been_prefetched:  # Does this exist?
  answer_count = len(question.answers.all())
else:
  answer_count = question.answers.count()

I’m using Django 1.4 if it matters. Thanks in advance.

Edit: added clarification that prefetch_related() isn’t the only way the answers could’ve been fetched.

Asked By: Chad

||

Answers:

Yes, Django stores the prefetched results in the _prefetched_objects_cache attribute of the parent model instance.

So you can do something like:

instance = Parent.objects.prefetch_related('children').all()[0]

try:
    instance._prefetched_objects_cache[instance.children.prefetch_cache_name]
    # Ok, it's pefetched
    child_count = len(instance.children.all())
except (AttributeError, KeyError):
    # Not prefetched
    child_count = instance.children.count()

See the relevant use in the django source trunk or the equivalent in v1.4.9

Answered By: Kevin Stone
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.