如何强制Django Admin使用select_related?

时间:2011-07-31 23:01:05

标签: django django-models django-admin django-orm

我的一个模特特别复杂。当我尝试在Django Admin中编辑它时,它会执行1042次查询并需要9秒以上的时间来处理。

我知道我可以用raw_id_fields取代一些下拉列表,但我认为更大的瓶颈是它没有按预期执行select_related()

我可以让管理网站这样做吗?

6 个答案:

答案 0 :(得分:30)

虽然jimbob博士的回答是有道理的,但根据我的需要,我能够简单地用一行代替覆盖get_queryset()方法,甚至可以选择外键的外键。也许这对某人有帮助。

class MyModelAdmin(admin.ModelAdmin):
    model = MyModel
    ...
    def get_queryset(self, request):
        return super(MyModelAdmin, self).get_queryset(request).select_related(
            'foreign_key1', 'foreign_key2__fk2_foreign_key')

答案 1 :(得分:19)

你可以试试这个

  def spreadsheet
    spreadsheet = GenerateSpreadsheet.generate(params[:id])
    send_file spreadsheet
  end

https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_select_related

答案 2 :(得分:11)

对于我的特定模型,当它们以表格形式显示时,特别慢的方面是通过ForeignKeys,而不是使用select_related调用,所以这是我要加速的部分。

查看相关的django来源,您会在django/contrib/admin/options.py中看到方法formfield_for_foreignkeys获取每个FK db_field并调用ForeignKey类的formfield方法,在django / db / models / fields / related / like中定义:

def formfield(self, **kwargs):
    db = kwargs.pop('using', None)
    defaults = {
        'form_class': forms.ModelChoiceField,
        'queryset': self.rel.to._default_manager.using(db).complex_filter(self.rel.limit_choices_to),
        'to_field_name': self.rel.field_name,
    }
    defaults.update(kwargs)
    return super(ForeignKey, self).formfield(**defaults)

由此我们看到,如果我们为db_field提供kwargs['queryset'],我们可以定义一个将使用select_related的自定义查询集(这可以由formfield_for_foreignkey提供)。

基本上我们要做的是用admin.ModelAdmin覆盖SelectRelatedModelAdmin,然后创建我的ModelAdmin子类SelectRelatedModelAdmin而不是admin.ModelAdmin

class SelectRelatedModelAdmin(admin.ModelAdmin):
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if 'queryset' in kwargs:
            kwargs['queryset'] = kwargs['queryset'].select_related()
        else:
            db = kwargs.pop('using', None)
            kwargs['queryset'] = db_field.rel.to._default_manager.using(db).complex_filter(db_field.rel.limit_choices_to).select_related()
        return super(SelectRelatedModelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

此代码示例不包括admin InlineManyToManyField s,或readonly_fields调用的函数中的foreign_key遍历或自定义select_related查询,但类似的方法应适用于那些例。

答案 3 :(得分:5)

在Django 2.0+中,提高ForeignKey和ManyToMany关系性能的一个好方法是使用autocomplete fields

这些字段不显示所有相关对象,因此加载的查询次数更少。

答案 4 :(得分:2)

为了完整起见,我想添加另一个最适合我的用例的选项。

正如其他人所指出的,问题通常是加载选择框的数据。 list_select_related 在这种情况下没有帮助。

如果您实际上不想通过管理员编辑外键字段,最简单的解决方法是将相应字段设为只读:

class Foo(admin.ModelAdmin):
    readonly_fields = ('foreign_key_field1','foreign_key_field2',)

您仍然可以显示这些字段,只是没有选择框,因此 Django 不需要从数据库中检索所有选择框选项。

答案 5 :(得分:1)

对于管理员编辑/更改特定项目的页面,外键选择框可能需要花费很长时间才能加载,从而改变django查询数据以获取外键的方式:

Django docs on Using formfield_for_foreignkey

假设我的foo模型上有一个名为Example的字段,我希望选择相关的bar对象:

class ExampleAdmin(admin.ModelAdmin):

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
            if db_field.name == "foo":
                kwargs["queryset"] = MachineTool.objects.select_related('bar')
            return super().formfield_for_foreignkey(db_field, request, **kwargs)