首页 > 解决方案 > 防止 Django 模板 base.py 解析内联中所有可能的外键关系

问题描述

我正在尝试在现有的 Django Admin 类上创建内联,但发现页面呈现时间太慢了。我已经确定问题出在 django templates/base.py “resolve”函数或后续渲染函数(Django 2.1)中。在所有情况下,似乎我的所有“跟踪”对象都在为该行中的所有表单加载。

如何通过更改代码来提高此功能的性能?我应该重组 Recording-->Tracklist-->TracklistTrack-->Track 关系吗?我可以以某种方式缓存我的模型对象吗?在内联中使用 .through 模型是不明智的吗?

为了开始发现问题,我开始使用分析器。在代码上运行计时器时,有问题的分析器导致的减速可以忽略不计。

可以看到,我22s的函数调用绝大多数是这样的:

ncalls tottime percall cumtime percall filename:lineno(function)

  123    0.001    0.000   18.194    0.148 django/forms/widgets.py:232(_render)
  123    0.002    0.000   18.190    0.148 django/forms/renderers.py:29(render)

848104 1.037 0.000 3.867 0.000 django/template/base.py:668(resolve)

我查看了建议覆盖“queryset”和“formfield_for_db”的堆栈溢出帖子。在 django 函数中打印上下文时,我可以看到我的所有曲目都通过 base.py 解析进行管道传输,大概这就是问题所在。

新的内联看起来像这样:

    model = Tracklist.tracks.through

    readonly_fields = ('tracklist', 'recording')
    fields = ('track', 'timestamp', 'order')

    @silk_profile(name='Slow Inline')
    def formfield_for_dbfield(self, db_field, **kwargs):
        formfield = super(TracklistInline, self).formfield_for_dbfield(db_field, **kwargs)
        if db_field.name in ['track', 'tracklist', 'recording']:
            # dirty trick so queryset is evaluated and cached in .choices
            formfield.choices = formfield.choices
        return formfield

    def queryset(self, request):
        return super(MyAdmin, self).queryset(request).select_related(
            'track').select_related('tracklist').select_related('recording')```

Where Tracklist looks like this:

```class Tracklist(Timestamped, models.Model):
    """Tracklist is a collection of tracks, owned by a recording

              1                     0...1
    Recording   -------------------       Tracklist

              1                     0...*
    Tracklist   -------------------       Track"""
    tracks = models.ManyToManyField(
        Track, through="TracklistTrack", blank=True)
    recording = models.OneToOneField(
        Recording, related_name='tracklist', null=True, blank=True,
        on_delete=models.CASCADE)```

and tracklist_track, my customised join table, looks like this:

```class TracklistTrack(models.Model):
    """Stores ordering for a Tracklist/Track relationship"""
    class Meta:
        indexes = [
            models.Index(fields=['recording']),
        ]

    tracklist = models.ForeignKey(Tracklist, on_delete=models.CASCADE)
    recording = models.ForeignKey(
        Recording, related_name='tracklistsTrack',
        null=True, blank=True, on_delete=models.CASCADE)
    track = models.ForeignKey(Track, on_delete=models.CASCADE)
    timestamp = models.IntegerField(blank=True, null=True)
    order = models.IntegerField(blank=True, null=True)```

(A Track is a simple model with two charfields only, but a Recording is a bit of a monster)

标签: djangodjango-templates

解决方案


对于任何拖网堆栈溢出的人,我使用 django 2.0 的自动完成字段非常简单地解决了这个问题:

class TracklistInline(admin.TabularInline):
    model = Tracklist.tracks.through

    list_display = ('recording', 'timestamp', 'order')
    autocomplete_fields = ('track',)
    exclude = ('tracklist',)
    extra = 0

@admin.register(Track)
class TrackAdmin(admin.ModelAdmin):
    form = TrackForm
    ordering = ['title']
    search_fields = ('artist', 'title')
    list_display = ('artist', 'title',)
    exclude = ('modified',)

推荐阅读