Optimize django request to attract foreign key and django-taggit relationship

I have a todo model defined below:

class Action(models.Model): name = models.CharField("Action Name", max_length=200, unique = True) complete = models.BooleanField(default=False, verbose_name="Complete?") reoccurance = models.ForeignKey(Reoccurance, blank=True, null=True, verbose_name="Reoccurance") notes = models.TextField("Notes", blank=True) tags = TaggableManager() class Reoccurance(models.Model): label = models.CharField("Label", max_length=50, unique = True) days = models.IntegerField("Days") 

I want to list all the pending actions:

 actions = Action.objects.filter(complete=False) 

My template action list loops:

 {% for action in actions %} <p>{{ action }}</p> {% if action.reoccurance %} <p>{{ action.reoccurance }}</p> {% endif %} {% for tag in action.tags.all %} <span>{{ tag }}</span>{% if not forloop.last %}, {% endif %} {% endfor %} {% endfor %} 

Using the django-debug-toolbar , I see that for each action I click on the database with {% if action.reoccurance%} and {% for the tag in action.tags.all%}.

Is there a better way to write my query so that the database does not ping for each iteration of the loop? I think this has something to do with select_related, but I'm not sure what to do with django-taggit .

Update I received part of the response. select_related really works, but I had to specify reoccurance, perhaps because I cannot use it for tags:

 actions = Action.objects.select_related('reoccurance').filter(complete=False) 

The problem still is that I ended up in the database for each "action.tags.all" in the template loop. Is it possible to use some prefetching on django-taggit?

+3
source share
2 answers

You can use prefetch_related to retrieve the tags, but you need to go around the "tags" property, because, as jdi says, this is a custom manager, not a true one. Instead, you can:

actions = Action.objects.select_related('reoccurance').filter(complete=False)\ .prefetch_related('tagged_items__tag')

Unfortunately, action.tags.all in your code template will not use prefetching and will eventually execute its own request - so you need to take a rather hacky step bypassing the "tag manager":

 {% for tagged_item in action.tagged_items.all %} <span>{{ tagged_item.tag }}</span>{% if not forloop.last %}, {% endif %} {% endfor %} 

(Ed .: If you get a QuerySet object that does not have the prefetch_related attribute, it means that you are using a version of Django below 1.4 where prefetch_related is not available.)

+1
source

The problem is that tags not a field, but a custom manager that lives at the class level and just executes queries.

I'm not sure if this will work on user managers, as it is intended for many-to-many and the like fields that create similar sets of queries. But if you are using django 1.4, you can try prefetch_related . He will execute one more request which will distribute relations and cache them.

Disclaimer again: I don't know if this works for managers

 actions = Action.objects.select_related('reoccurance').filter(complete=False)\ .prefetch_related('tags') 
-one
source

All Articles