views:

37

answers:

1

I have the following models:

class Territory(models.Model):
    name = models.CharField(max_length=30)
    power = models.ForeignKey(Power, null=True, blank=True)
    is_supply = models.BooleanField()

class Subregion(models.Model):
    territory = models.ForeignKey(Territory)
    subname = models.CharField(max_length=10, blank=True)
    sr_type = models.CharField(max_length=1, choices=SUBREGION_CHOICES)
    init_unit = models.BooleanField()
    borders = models.ManyToManyField("self", null=True, blank=True)

    def __unicode__(self):
        if self.subname:
            return u'%s (%s)' % (self.territory.name, self.subname)
        else:
            return u'%s [%s]' % (self.territory.name, self.sr_type)

The problem is that when rendering a ModelFormSet, each form of which has 3 ModelChoiceFields containing all 120 Subregions, an individual SELECT query is generated for each Subregion in each widget. According to my Postgres logs, over 1000 queries are being generated for a simple 3-form formset, with a noticeable effect on page load times.

So, is there a reasonable way to do a single large query that will cache all of the information that Subregion.__unicode__() wants?

A: 

Have you tried using select_related()? This will help you with the extra look ups for territory.name. The borders will be skipped because select_related()'s default is to not follow foreign keys where null=True. You can try being explicit with the names of the related objects if you need to get the borders as well: Subregion.objects.select_related('territory', 'borders').all().

Take this for a spin in your app's ./manage.py shell and see if select_related() helps:

>>> from django import db
>>> db.reset_queries()
>>> Subregion.objects.all()
# ...
>>> len(db.connection.queries)
# some big number
>>> db.reset_queries()
>>> Subregion.objects.select_related().all()
# ...
>>> len(db.connection.queries)
# a smaller number
istruble
I had previously tried using `select_related` by constructing a queryset with it, then passing that queryset to all of the `ModelChoiceFields` using a `formfield_callback`. No difference in number of queries. However, your code above did have an effect (21 vs 1).
Jeff Bradberry
...And this would be because I made a mistake in my callback function. Yes, `select_related` is sufficient, taking it from 1136 select queries to only 56. Much better, thanks.
Jeff Bradberry