Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

python

django

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.

like image 917
Chad Avatar asked Oct 29 '13 03:10

Chad


1 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

like image 87
Kevin Stone Avatar answered Oct 14 '22 03:10

Kevin Stone