Search code examples
pythondjangodjango-1.7

Query prefetched objects without using the db?


I'm getting multiple objects with prefetched relations from my db:

datei_logs = DateiLog.objects.filter(user=request.user)
                             .order_by("-pk")
                             .prefetch_related('transfer_logs')

transfer_logs refers to this:

class TransferLog(models.Model):
  datei_log = models.ForeignKey("DateiLog", related_name="transfer_logs")
  status = models.CharField(
                    max_length=1,
                    choices=LOG_STATUS_CHOICES,
                    default='Good'
                    ) 
  server_name = models.CharField(max_length=100, blank=True, default="(no server)")
  server = models.ForeignKey('Server')

  class Meta:
    verbose_name_plural = "Transfer-Logs"

  def __unicode__(self):
    return self.server_name

Now I want to get all the TransferLogs that have a status of "Good". But I think if I do this:

datei_logs[0].transfer_logs.filter(...)

It queries the db again! Since this happens on a website with many log entries I end up with 900 Queries!

I use:

datei_logs[0].transfer_logs.count()

As well and it causes lots of queries to the db too!

What can I do to "just get everything" and then just query an object that holds all the information instead of the db?


Solution

  • Since you're on Django 1.7 you can use the new Prefetch() objects to specify the queryset you want to use for the related lookup.

    queryset = TransferLog.objects.filter(status='Good')
    datei_logs = DateiLog.objects.filter(user=request.user)
                                 .order_by("-pk")
                                 .prefetch_related(Prefetch('transfer_logs', 
                                                            queryset=queryset,
                                                            to_attr='good_logs'))
    

    Then you can access datei_logs[0].good_logs and check len(datei_logs[0].good_logs).

    If you're interested in multiple statuses, you can just use multiple Prefetch objects. But if you're going to get all the logs anyway, you might as well stick to your original query and then split the logs up in Python, rather than calling filter().