为什么Django admin中的list_select_related在这种情况下不起作用?

12 投票
3 回答
13087 浏览
提问于 2025-04-17 17:55

我有一个 ModelAdmin 类,它的 list_display 中包含一个外键字段。但是,这个模型的管理列表页面每一行都要进行数百次查询,每行都要从另一个表获取数据,而不是使用连接查询(select_related())。

根据Django的文档,你可以在你的ModelAdmin中添加一个属性 list_select_related = True 来解决这个问题,但对我来说似乎完全没用。这个StackOverflow的问题似乎也遇到了类似的问题,但他的解决办法不太清楚,而且在我这里也不管用。

这是我模型和模型管理的简化版本:

class Device(models.Model):
    serial_number = models.CharField(max_length=80, blank=True, unique=True)
    label = models.CharField(max_length=80, blank=True)

    def __str__(self):
        s = str(self.serial_number)
        if self.label:
            s += ': {0}'.format(self.label)
        return s

class Event(models.Model):
    device = models.ForeignKey(Device, null=True)
    type = models.CharField(max_length=40, null=False, blank=True, default='')

class EventAdmin(admin.ModelAdmin):
    list_display = ('__str__', 'device')
    list_select_related = True

然而,添加 list_selected_related = True 并没有改变任何事情。我仍然看到很多这样的查询,而不是一个SQL连接:

Lots of queries, not a join

有没有人知道为什么Django管理界面似乎忽略了我的list_select_related,导致进行了N次查询?我使用的是Python 2.7和Django 1.3.3。

3 个回答

0

虽然一般情况下使用 select_related 是比较好的选择,但有时候我们需要更多的控制权,这时候重写 get_queryset 就更合适了。这是对 Daniel Roseman 答案的一个更现代的版本:

这里的 foobar 是外键字段:

    def get_queryset(self, request):
        qs = super().get_queryset(request)
        return qs.select_related('foo', 'foo__bar').only('foo__field1', 'foo__bar__field2')
10

自从Django 1.6版本开始,list_select_related这个选项可以接受一个布尔值、列表或者元组,里面包含你想要在select_related()调用中包含的字段名。

所以现在你可以这样使用:

class EventAdmin(admin.ModelAdmin):
    list_display = ('__str__', 'device')
    list_select_related = ['device']
19

这里的问题是,设置 list_select_related = True 只是给查询添加了一个基本的 select_related(),但是这个调用默认情况下不会跟踪那些设置了 null=True 的外键(ForeignKey)。所以解决办法是自己定义一下 changelist 使用的查询集,并明确指定要跟踪的外键:

class EventAdmin(admin.ModelAdmin):
    list_display = ('__str__', 'device')
    def queryset(self, request):
        return super(EventAdmin, self).queryset(request).select_related('device')

撰写回答