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
相关产品推荐
相关产品推荐

