Django管理页面内嵌模型加载非常缓慢

6
我有一个Django管理页面,其中包含一个单一的嵌入模型。当嵌入模型有许多条目(例如75个)时,页面加载非常缓慢(大约需要30秒)。即使我在内联模型上排除所有字段,只渲染名称,这也是正确的。移除内嵌模型可以使页面快速加载(几秒钟内)。
我该如何让这个页面加载更快?

我最终使用了这个通用解决方案 https://dev59.com/mJzha4cB1Zd3GeqPHZjv#44932400 - vishes_shell
2个回答

17

有两个方法可以加快此页面的加载速度:

  1. DEBUG = False
  2. 缓存用于渲染内联模型的数据库查询。

将 DEBUG 设为 False

此页面加载缓慢的原因是 Django 管理员为每个内联模型实例呈现子模板。即使其中内容很少,75 个内联模型实例会呈现 75 个额外的模板。虽然渲染模板通常非常快,但如果您使用了 DEBUG = True,则对于您呈现的每个模板,都会产生额外的开销,因为 Django 会注入用于其调试模式错误页面的额外上下文(在我的系统上每个模板约为 0.4 秒)。这种额外开销通常不会引起注意,因为您通常只会为单个页面呈现少量模板。但是,当您呈现 75 个模板时,这加起来就会导致明显的延迟(75 * 0.4 秒 = 30 秒)。


缓存数据库查询

至少在 Django 1.10 中,内联模型将为每个内联模型实例进行任何必要的数据库查询以呈现它。如果您的内联模型具有内联模型上的任何外键,则在具有缓慢的数据库连接时,这可能会非常低效。技巧是在初始化内联类的实例(使用 get_formsets_with_inlines)时运行用于这些外键选择的数据库查询,然后将这些字段的选择项替换为缓存值,以防止重复此数据库查询(使用formfield_for_foreignkey)。

class Manufacturer(models.Model):
    ...

class OperatingSystem(models.Model):
    ...

class Computer(models.Model):
    manufacturer = models.ForeignKey(Manufarturer)
    owner = models.ForeignKey(User)
    operating_system = models.ForeignKey(OperatingSystem)
    ...


class ComputerAdmin(admin.StackedInline):
    model = Computer

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        field = super(ComputerAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
        if db_field.name == "owner" and hasattr(self, "cached_owners"):
            field.choices = self.cached_owners
        elif db_field.name == "manufacturer" and hasattr(self, "cached_manufacturers"):
            field.choices = self.cached_manufacturers
        elif db_field.name == "operating_system" and hasattr(self, "cached_operating_systems"):
            field.choices = self.cached_operating_systems
        return field


class ManufacturerAdmin(admin.ModelAdmin):
    inlines = (ComputerAdmin,)

    def get_formsets_with_inlines(self, request, obj=None):
        for inline in self.get_inline_instances(request, obj):
            inline.cached_owners = [(i.pk, str(i)) for i in User.objects.all()]
            inline.cached_manufacturers = [(i.pk, str(i)) for i in Manufacturer.objects.all()]
            inline.cached_operating_systems = [(i.pk, str(i)) for i in OperatingSystem.objects.all()]
            yield inline.get_formset(request, obj), inline

我使用了这个解决方案来加快我的内联速度,但现在我遇到了一个问题,即内联中的元素没有显示其选择中的实际值。我有一个模型:Serial_Roll,其中包含许多标签。每个标签都有重量、口味和状态。(这些都是外键)每个卷都有1000多个标签。我希望我的内联显示每个FK的当前值,但也有其他选择在选择列表中。我需要改变这个吗? - Andy Poquette
@AndyPoquette 这只是覆盖了内联的FK选择,应该保留当前值的选择。如果我在发布的解决方案中有错误,请告诉我。 - Zags
我相信这不是你的问题,而是我的问题。我选择了另一种解决方案来解决我的问题,但还是谢谢你! - Andy Poquette
@AndyPoquette,另一个解决方案是什么? - James Parker

0

请查看我的解决方案:https://dev59.com/sl0b5IYBdhLWcg3wENmK#51391915

我的管理员类如下所示:

class ItemBazarInlineAdmin(AdminInlineWithSelectRelated):
    model = ItemBazar

    list_select_related = (
        'alumno_item__alumno__estudiante__profile',
        'alumno_item__item__anio_lectivo',
        'comprobante__forma_pago',
    )


class ComprobanteAdmin(AdminWithSelectRelated):
    list_display = ('__str__', 'total', 'estado', 'fecha_generado', 'forma_pago', 'tipo', )
    list_filter = ('estado', 'forma_pago', )

    list_select_related = ('forma_pago', )
    inlines = (ItemServicioInlineAdmin, )

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接