【问题标题】:Django admin change form load quite slowDjango 管理员更改表单加载速度很慢
【发布时间】:2013-05-21 06:08:19
【问题描述】:

我的一个 Django 网站具有以下数据库模型: 在 Django App 中“常见”:

class Collection(models.Model):
    name = models.CharField(max_length = 255, unique = True)
    _short_name = models.CharField(db_column="short_name", max_length = 32, blank=True)

class Particle(models.Model):
    content = models.TextField(blank=False)
    owner = models.ForeignKey(Collection)
    order = models.IntegerField(null=True, blank=True)

在 Django 应用“情景喜剧”中:

class Media(models.Model):
    name = models.CharField(max_length = 248)
    _short_name = models.CharField(db_column="short_name", max_length = 32, blank=True)
    capital = models.CharField(max_length = 1)
    description = models.TextField(blank=True)
    progress = models.CharField(max_length = 32, blank=True, null=True)

class Relation(models.Model):
    name = models.CharField(max_length = 128)
    _short_name = models.CharField(db_column="short_name", max_length = 32, blank=True)
    description = models.TextField(blank=True)
    parent = models.ForeignKey('self', blank=True, null=True)
    order = models.IntegerField(blank=True, null=True)
    particle = models.ForeignKey(Particle, blank=True, null=True)
    media = models.ForeignKey(Media, blank=True, null=True)

简而言之,模型类 Relation 对其他表有 3 个外键。 问题是,当我使用 Django Admin 更改单个关系时,页面 (change_form) 加载速度相当慢。 后来,我将模型类 Relation 更改为:

class Relation(models.Model):
    name = models.CharField(max_length = 128)
    _short_name = models.CharField(db_column="short_name", max_length = 32, blank=True)
    description = models.TextField(blank=True)
    order = models.IntegerField(blank=True, null=True)
    parent_id = models.IntegerField(blank=True, null=True)
    particle_id = models.IntegerField(blank=True, null=True)
    media_id = models.IntegerField(blank=True, null=True)

修改将外键更改为 IntegerFields,因此它禁用了 Django ORM 系统内部的一些魔法,现在更改表单页面加载速度非常快。 我的问题是,“django orm 中的禁用魔法”是什么?什么可能导致问题?

【问题讨论】:

    标签: python django performance django-models django-admin


    【解决方案1】:

    在 admin.py 中

    from django.contrib import admin
    
    class RelationAdmin(admin.ModelAdmin):
           raw_id_fields = ('Media','Particle',)
    
    admin.site.register(Relation, RelationAdmin)
    

    这会在表单中产生一个漂亮的小 UI 元素,并显着提高性能,因为它不必在选择框中加载大量选项。

    【讨论】:

    • 谢谢!这个小技巧让我的管理网站更加实用!
    • 有时您甚至不想编辑管理表单中的字段,只需查看它们,您也可以添加readonly_fields = ('Media','Particle',)
    • 这太棒了!
    • documentation for raw_id_fields
    【解决方案2】:

    这不是 django Orm 的魔力。这是形式的魔法。 当您在 Model 中创建外键时,然后在 ModelForm 中创建一个 ModelChoiceField,它具有 ForeignKey Model 的所有选择。并且 django Admin 使用 Form 的所有属性来创建 HTML。 所以使用这个代码。

    from django import forms
    class RelationForm(forms.ModelForm):
        parent = forms.ChoiceField(required=False,
                                  choices=Relation.objects.values_list('id', 'name'))
        particle = forms.ChoiceField(required=False,
                                  choices=Particle.objects.values_list('id', 'content'))
        media = forms.ChoiceField(required=False,
                                  choices=Media.objects.values_list('id', 'name'))
    
        class Meta:
            model = Relation 
    

    在 Admis 网站中

    from django.contrib import admin
    class RelationAdmin(admin.ModelAdmin):
        form = RelationForm
        model = Relation
    

    您还可以在表单中缓存选择传递。

    【讨论】:

    • 非常感谢!它有效,现在我可以再次使用 ForeignKey,它更方便。
    【解决方案3】:

    我敢打赌这个问题是由于你的ForeignKey。默认情况下,django 为每个外键呈现一个<select> 元素。

    如果您有数千行,这很容易使您的 HTML / DOM 膨胀,我注意到浏览器开始在 <select> 标记中呈现 20k 个项目时出现问题。

    要解决此问题,请考虑覆盖您的管理表单而不使用默认小部件。

    【讨论】:

      【解决方案4】:

      在我的情况下,缓慢主要是由过时的django-debug-toolbar (v1.7) 引起的。 debug_toolbar.middleware.DebugToolbarMiddleware 会导致管理页面实际上需要 20 分钟才能加载,如果它包含一个带有几百个选项的 ForeignKey 字段。将工具栏升级到 v1.8 解决了所有问题。也许这对某人有帮助。

      【讨论】:

      • 这发生在自定义表单(非管理员)上,这对我来说是修复。工具栏的“模板”部分似乎是罪魁祸首。我不明白为什么
      【解决方案5】:

      另一种可能性:

      正如@yuji-tomita-tomita elsewhere on this page 所述,管理员change_formForeignKey 字段呈现<select> 元素。

      这是使用Select widget 完成的。

      默认情况下,<select> 元素中的每个<option> 都有一个文本标签,该标签基于ForeignKey 指向的模型的__str__() 方法。这可以在source 中看到forms.models.ModelChoiceField

      因此,如果您的 Model.__str__() 方法使用相关字段,当管理表单填充选择选项时,您将获得额外的数据库查询。

      例如,基于 OP,假设我们会这样:

      class Particle(models.Model):
          ...
          owner = models.ForeignKey(Collection, on_delete=models.CASCADE)
          ...
      
          def __str__(self):
              return 'particle owned by {}'.format(self.owner.name)
      

      然后管理员需要查询数据库以获取每个Particle 选项的相关Collection.name 值。

      管理员没有有效地做到这一点。

      如果你有很多 Particle 对象,那很快就会变得非常昂贵。

      【讨论】:

        【解决方案6】:

        如果 ForeignKey 模型的记录太多,它可能会冻结您的编辑表单。 最好的开始方法是限制应该在表单上显示的字段,并逐渐/一个接一个地添加其他字段,检查哪个字段使表单变慢。

        from django.contrib import admin
        class RelationAdmin(admin.ModelAdmin):
               fields = ('name',)
        admin.site.register(Relation, RelationAdmin)
        

        然后在添加导致问题的字段之后,例如媒体,表格将再次冻结。因此,如果您仍然需要表单上的此字段,您可以使用 Vishal Shah 的回答与 raw_id_fields = ('Media', )

        【讨论】:

          【解决方案7】:

          Django 现在支持autocomplete_fields,它使用 Select2 进行输入。可以如下配置:

          from django.contrib import admin
          
          class RelationAdmin(admin.ModelAdmin):
                 autocomplete_fields = ('Media','Particle',)
          
          admin.site.register(Relation, RelationAdmin)
          

          【讨论】:

            猜你喜欢
            • 2019-04-25
            • 2016-09-24
            • 2015-05-15
            • 1970-01-01
            • 1970-01-01
            • 2013-08-18
            • 2018-08-08
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多