Django的__unicode__和外键非常慢

2 投票
2 回答
1109 浏览
提问于 2025-04-17 12:57

如果我写了类似这样的代码:

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")

然后我尝试为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)

结果是这个页面加载时间可能超过30秒。通过一些调试,我发现它不断地对ChipStepping和Chip进行重复的单个查询,而且每次查询的条件都是一样的,而不是聪明地构建一个可以一次性查找所有数据的查询。

如果我从ChipStepping和ComponentType的unicode函数中移除外键引用,这个问题会有所减轻。

如果在ComponentVendorProducts中有足够多的条目,当我在管理页面上点击某个供应商时,页面加载可能需要几分钟!

有没有办法减少管理页面对数据库的访问次数呢?

2 个回答

0

你可能还想看看这里的建议:http://blog.ionelmc.ro/2012/01/19/tweaks-for-making-django-admin-faster/

看起来每一行的数据选项都会被重新计算,所以你可以使用 formfield_for_dbfield 来缓存这些选项,正如链接中所建议的那样。这样做可以避免每次都去数据库获取外键的下拉框/选择框的数据。

6

你的问题出在每次调用一个 ComponentType 实例的 __unicode__ 方法时,Django 都会去数据库查询一次。

解决这个问题有两个办法:

  1. 你可以重写 ProductInlinequeryset 方法,加入 select_related('ChipStepping')(适用于 Django 1.3 及以上版本)。
  2. 另外,如果你想在其他地方也解决这个问题,可以修改 ComponentType 的默认管理器(objects)的 get_query_set 方法,让它也包含 select_related 的调用。

撰写回答