如何强制 Django Admin 使用 select_lated?

发布于 2024-11-27 11:28:19 字数 194 浏览 1 评论 0原文

我的一个模型特别复杂。当我尝试在 Django Admin 中编辑它时,它执行 1042 个查询,处理时间超过 9 秒。

我知道我可以用 raw_id_fields 替换一些下拉菜单,但我认为更大的瓶颈是它没有执行应有的 select_lated()

我可以通过管理网站来执行此操作吗?

One of my models is particularily complex. When I try to edit it in Django Admin it performs 1042 queries and takes over 9 seconds to process.

I know I can replace a few of the drop-downs with raw_id_fields, but I think the bigger bottleneck is that it's not performing a select_related() as it should.

Can I get the admin site to do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

一笔一画续写前缘 2024-12-04 11:28:20

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

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

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

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

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

For the sake of completeness, I would like to add another option that was the most suitable for my use case.

As others have pointed out, the problem is often loading the data for select boxes. list_select_related does not help in this case.

In case you don't actually want to edit the foreign key field via admin, the easiest fix is making the respective field readonly:

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

You can still display these fields, there will simply not be a select box, hence Django does not need to retrieve all the select box options from the database.

中性美 2024-12-04 11:28:19

你可以尝试这个

class Foo(admin.ModelAdmin):
    list_select_related = (
        'foreign_key1',
        'foreign_key2',
    )

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

you can try this

class Foo(admin.ModelAdmin):
    list_select_related = (
        'foreign_key1',
        'foreign_key2',
    )

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

救星 2024-12-04 11:28:19

虽然 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')

Although dr jimbob's answer makes sense, for my needs, I was able to simply override the get_queryset() method with a one-liner, even selecting a foreign key's foreign key. Maybe this could be helpful to someone.

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')
二智少女猫性小仙女 2024-12-04 11:28:19

对于我的特定模型,特别慢的方面是在表单中显示外键时,它们不会使用 select_lated 调用,所以这就是我要加快的部分。

查看相关的 django 源代码,您可以在 django/contrib/admin/options.py 中看到方法 formfield_for_foreignkeys 获取每个 FK db_field 并调用ForeignKey 类的 formfield 方法,该方法定义在 django/db/models/fields/lated/ 中,如下所示:

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_lated 的自定义查询集(这可以由 formfield_for_foreignkey 提供>)。

所以基本上我们想要做的是用 SelectRelatedModelAdmin 覆盖 admin.ModelAdmin ,然后使我们的 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 ,或由 调用的函数中的foreign_key 遍历readonly_fields 或自定义 select_lated 查询,但类似的方法应该适用于这些情况。

For my particular model, the particularly slow aspect is going through ForeignKeys when they were being displayed in forms, which aren't called using select_related, so that's the part I'm going to speed up.

Looking through the relevant django source, you see in django/contrib/admin/options.py that the method formfield_for_foreignkeys takes each FK db_field and calls the ForeignKey class's formfield method, which is defined in 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)

From this, we see if we provide the db_field with a kwargs['queryset'] we can define a custom queryset that will be use select_related (this can be provided by formfield_for_foreignkey).

So basically what we want to do is override admin.ModelAdmin with SelectRelatedModelAdmin and then make our ModelAdmin subclasses of SelectRelatedModelAdmin instead of 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)

This code sample doesn't cover admin Inlines or ManyToManyFields, or foreign_key traversal in functions called by readonly_fields or custom select_related queries, but a similar approach should work for those cases.

阿楠 2024-12-04 11:28:19

在 Django 2.0+ 中,提高外键和多对多关系性能的一个好方法是使用 自动完成字段

这些字段不显示所有相关对象,因此加载的查询要少得多。

In Django 2.0+, a good way to improve performance of ForeignKey and ManyToMany relationships is to use autocomplete fields.

These fields don't show all related objects and therefore load with many fewer queries.

腻橙味 2024-12-04 11:28:19

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

有关使用的 Django 文档formfield_for_foreignkey

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

class ExampleAdmin(admin.ModelAdmin):

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

For the admin edit/change a specific item page, foreign key select boxes may take a long time to load, to alter the way django queries the data for the foreign key:

Django docs on Using formfield_for_foreignkey

Say I have a field called foo on my Example model, and I wish to select ralated bar objects:

class ExampleAdmin(admin.ModelAdmin):

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
            if db_field.name == "foo":
                kwargs["queryset"] = Example.objects.select_related('bar')
            return super().formfield_for_foreignkey(db_field, request, **kwargs)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文