Django __unicode__ and FK is very slow

2019-07-16 09:05发布

问题:

if I write something like

class Chip(models.Model):
  name      = models.CharField(max_length=16)
  shortname = models.CharField(primary_key=True, unique=True, max_length = 16)

  def __unicode__(self):
    return self.shortname

class ChipStepping(models.Model):

  stepping = models.CharField (max_length=16)
  ChipShortname = models.ForeignKey('Chip', db_column="ChipShortname")

  def __unicode__(self):
    return "%s:%s" % (self.ChipShortname, self.stepping)

class ComponentType(models.Model):
  name         = models.CharField (max_length=32)
  ChipStepping = models.ForeignKey('ChipStepping', db_column="ChipStepping")

  def __unicode__(self):
    return "%s(%s)" % (self.name, self.ChipStepping);

class ComponentVendor(models.Model):
  name     = models.CharField      (unique=True, max_length=16)
  products = models.ManyToManyField('ComponentType', through='ComponentVendorProduct', related_name='vendors')

  def __unicode__(self):
    return "%s" % (self.name)

class ComponentVendorProduct(models.Model):
  ComponentVendor = models.ForeignKey('ComponentVendor', db_column="ComponentVendor")
  ComponentType   = models.ForeignKey('ComponentType'  , db_column="ComponentType")

And try to create an admin page for ComponentVendor

class ProductInline(admin.TabularInline):
  model = ComponentVendor.products.through
  extra = 0

class ComponentVendorAdmin(admin.ModelAdmin):
  inlines = [ProductInline]
  list_filter = ['products__name']
  exclude = ['products']

admin.site.register(ComponentVendor, ComponentVendorAdmin)

The resulting page can take upwards of 30 sec. to load From some debugging I've done, I found that it repeatedly makes redundant singular queries for ChipStepping and then Chip, with the same argument in the where clause instead of intelligently building a query that can lookup all the data.

This problem is reduced if I remove the foreign key references from the unicode functions of ChipStepping and ComponentType

If there are enough entries in ComponentVendorProducts for a vendor I click on in the admin page, the page can take several minutes!

Is there a way I can reduce the number of database hits on the admin page?

回答1:

Your problem comes from the fact that Django is doing a DB call everytime you call __unicode__ on a ComponentType instance.

You have two solutions to your issue:

  1. You override your ProductInline's queryset method to include select_related('ChipStepping') (Django 1.3 and superior).
  2. Alternatively, if you want to fix the issue elsewhere too, you might want to change your ComponentType's default manager (objects) get_query_set method to have it include the select_related call.


回答2:

you might also want to checkout the suggestion given here: http://blog.ionelmc.ro/2012/01/19/tweaks-for-making-django-admin-faster/

It seems that the choices are evaluated for every row, so using formfield_for_dbfield you can cache the choices as suggested in the link. This saves going to the db for rendering every single dropdown/select box for foreign_keys