Django自定义Volume多输入字段报错,求排查解决
问题描述
尝试创建一个包含长度、宽度、高度和单位4部分的自定义Volume字段,计划通过继承models.JSONField实现,现有代码如下:
inventory/models.py
from django.db import models from tpt.fields import VolumeField class Measurement(models.Model): volumne = VolumeField()
tpt/fields.py
from django.db import models from tpt import forms class VolumeField(models.JSONField): description = 'Package volume field in 3 dimensions' def __init__(self, length=None, width=None, height=None, units=None, *args, **kwargs): self.widget_args = { "length": length, "width": width, "height": height, "unit_choices": units, } super(VolumeField, self).__init__(*args, **kwargs) def formfield(self, **kwargs): defaults = {"form_class": forms.VolumeWidgetField} defaults.update(kwargs) defaults.update(self.widget_args) return super(VolumeField, self).formfield(**defaults)
tpt/forms.py
import json from django import forms class VolumeWidget(forms.widgets.MultiWidget): def __init__(self, attrs=None): widgets = [forms.NumberInput(), forms.NumberInput(), forms.NumberInput(), forms.TextInput()] super(VolumeWidget, self).__init__(widgets, attrs) def decompress(self, value): if value: return json.loads(value) else: return [0, 0, 0, ''] class VolumeWidgetField(forms.fields.MultiValueField): widget = VolumeWidget def __init__(self, *args, **kwargs): list_fields = [forms.fields.CharField(max_length=8), forms.fields.CharField(max_length=8), forms.fields.CharField(max_length=8), forms.fields.CharField(max_length=4)] super(VolumeWidgetField, self).__init__(list_fields, *args, **kwargs) def compress(self, values): return json.dumps(values)
服务器可正常启动,但在Admin后台添加Measurement模型条目时出现如下错误:
[13/May/2024 11:57:59] "GET /admin/inventory/measurement/ HTTP/1.1" 200 12250 Internal Server Error: /admin/inventory/measurement/add/ Traceback (most recent call last): ... TypeError: Field.__init__() got an unexpected keyword argument 'encoder'
尝试继承models.CharField测试时,先提示CharFields must define a 'max_length' attribute.,添加max_length后又出现类似的TypeError: Field.__init__() got an unexpected keyword argument 'max_length'错误,请问哪里出错了?
解决方案
错误根源
问题出在formfield方法中,你将模型字段的固有参数(比如JSONField的encoder、CharField的max_length)和自定义的widget_args混合后传递给了表单字段,但表单字段并不认识这些模型层面的参数,因此抛出“意外关键字参数”的错误。
当调用父类的formfield方法时,Django会自动携带模型字段的相关参数,而你后续把widget_args合并到defaults中,导致这些无关参数被传递给表单字段的构造函数,触发报错。
修复代码
修改tpt/fields.py中的formfield方法,分离模型参数和表单参数:
def formfield(self, **kwargs): # 仅提取表单字段需要的自定义参数 form_specific_args = {k: v for k, v in self.widget_args.items() if v is not None} # 设置表单类并合并用户传入的表单参数 form_specific_args["form_class"] = forms.VolumeWidgetField form_specific_args.update(kwargs) # 调用父类方法,仅传递表单相关参数 return super(VolumeField, self).formfield(**form_specific_args)
额外优化点
- 修正字段拼写:
Measurement模型中的volumne改为volume,避免拼写错误。 - 数值字段类型修正:长度、宽度、高度改用
FloatField更合理,替换tpt/forms.py中VolumeWidgetField的list_fields:
list_fields = [ forms.FloatField(), forms.FloatField(), forms.FloatField(), forms.CharField(max_length=4) ]
- 单位选择规范化:如果单位是固定选项,把
TextInput换成Select组件,限制输入:
# 修改VolumeWidget的widgets定义 widgets = [ forms.NumberInput(), forms.NumberInput(), forms.NumberInput(), forms.Select(choices=[('cm', '厘米'), ('m', '米'), ('in', '英寸')]) ]
同时对应调整表单字段:
# 在list_fields中替换第四个字段 forms.ChoiceField(choices=[('cm', '厘米'), ('m', '米'), ('in', '英寸')])
内容的提问来源于stack exchange,提问作者Stuart Clarke
相关产品推荐
相关产品推荐

