You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django Admin列表设list_editable含Money字段加载缓慢,求优化方案

Django Admin中django-money字段列表编辑性能优化问题

场景描述

模型中使用django-money定义了三个金额字段:

class MyModel(models.Model):
    price_low = MoneyField(
        max_digits=10,
        default=0,
        decimal_places=2,
        null=True,
        blank=True,
        default_currency="USD",
    )
    price_medium = MoneyField(
        max_digits=10,
        default=0,
        decimal_places=2,
        null=True,
        blank=True,
        default_currency="USD",
    )
    price_high = MoneyField(
        max_digits=10,
        default=0,
        decimal_places=2,
        null=True,
        blank=True,
        default_currency="USD",
    )

在Admin配置中将这些字段设为列表页可编辑:

class IssueAdmin(admin.ModelAdmin):
    list_editable = (
        "price_low",
        "price_medium",
        "price_high"
    )
    list_per_page = 20

当前数据库有40万条记录,开启列表编辑后页面加载耗时约6秒;移除list_editable中的这些字段,页面加载耗时仅1.5秒,需优化该场景下的性能。

优化方案

1. 自定义轻量级表单控件替代默认实现

django-money默认表单控件会处理额外的货币逻辑,渲染时产生冗余开销。可以自定义仅处理金额输入的控件,复用模型字段验证逻辑但简化渲染:

from django import forms
from djmoney.models.fields import MoneyField

class SimpleMoneyInput(forms.TextInput):
    def render(self, name, value, attrs=None, renderer=None):
        # 仅渲染金额部分,货币固定为USD
        if value is not None:
            value = value.amount
        return super().render(name, value, attrs, renderer)

class SimpleMoneyField(forms.DecimalField):
    widget = SimpleMoneyInput

    def prepare_value(self, value):
        if value is not None:
            return value.amount
        return super().prepare_value(value)

# 在Admin中覆盖字段的表单控件
class IssueAdmin(admin.ModelAdmin):
    list_editable = ("price_low", "price_medium", "price_high")
    list_per_page = 20

    def formfield_for_dbfield(self, db_field, request, **kwargs):
        if db_field.name in ["price_low", "price_medium", "price_high"]:
            kwargs["form_class"] = SimpleMoneyField
            kwargs.pop("widget", None)
        return super().formfield_for_dbfield(db_field, request, **kwargs)

2. 优化数据库查询逻辑

即使设置了list_per_page=20,django-money字段可能触发额外关联查询。通过only方法限定加载字段,减少数据库查询开销:

class IssueAdmin(admin.ModelAdmin):
    list_editable = ("price_low", "price_medium", "price_high")
    list_per_page = 20

    def get_queryset(self, request):
        qs = super().get_queryset(request)
        # 仅加载必要字段,避免冗余查询
        return qs.only("id", "price_low", "price_medium", "price_high")

3. 隐藏固定货币的选择控件

如果所有记录的货币固定为USD,直接隐藏货币选择下拉框,减少渲染元素数量:

class IssueAdmin(admin.ModelAdmin):
    list_editable = ("price_low", "price_medium", "price_high")
    list_per_page = 20

    def formfield_for_dbfield(self, db_field, request, **kwargs):
        field = super().formfield_for_dbfield(db_field, request, **kwargs)
        if db_field.name in ["price_low", "price_medium", "price_high"]:
            # 隐藏货币选择控件
            field.widget.widgets[1].attrs["style"] = "display:none;"
            # 固定货币值
            field.widget.widgets[1].initial = "USD"
        return field

4. 异步渲染列表编辑控件

利用Django Admin的JS钩子,页面初始加载时仅显示字段内容,加载完成后再异步替换为可编辑输入框,避免阻塞初始渲染:

  • 先将这些字段设为普通显示字段,不加入list_editable
  • 自定义Admin模板,通过JS在页面加载完成后,将指定字段的显示内容替换为输入框
  • 提交修改时通过AJAX批量处理,自定义视图完成批量保存

内容的提问来源于stack exchange,提问作者James Parker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 07:05:29