Django管理员改变表单加载速度很慢

时间:2013-05-26 01:05:55

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

我的一个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 App“情景喜剧”中:

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更改单个Relation时,页面(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)

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

6 个答案:

答案 0 :(得分:16)

在admin.py

from django.contrib import admin

class RelationAdmin(admin.ModelAdmin):
       raw_id_fields = ('Media','Particle',)

admin.site.register(Relation, RelationAdmin)

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

答案 1 :(得分:10)

这不是django Orm的魔力。这是Form的魔力。 在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

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

答案 2 :(得分:6)

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

如果你有成千上万的行,这很容易开始膨胀你的HTML / DOM,我注意到浏览器开始废弃在<select>标签中呈现的20k项目。

要修复它,请查看覆盖管理表单而不使用默认小部件。

答案 3 :(得分:1)

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

答案 4 :(得分:0)

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

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

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

答案 5 :(得分:0)

另一种可能性:

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

这是通过Select widget完成的。

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

因此,如果您的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)

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

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

如果您有许多Particle对象,那么它们很快就会变得非常昂贵。