(这是所有伪代码,无法保证运行。)
我正在尝试制作一个" django管理表格生成器功能"输出django形式。当前的用例是编写可重用的代码,禁止管理员将字段留空,而不是marking these fields as non-nullable。
假设存在一个模型Foo
,其中有一些可以为空的字段:
class Foo(Model):
field1 = FloatField(null=True, blank=True, default=0.0)
field2 = FloatField(null=True, blank=True, default=0.0)
field3 = FloatField(null=True, blank=True, default=0.0)
以及相应的FooAdmin
和FooForm
,以便管理员无法将这些字段设为None
。
class FooAdmin(ModelAdmin):
class FooForm(ModelForm):
class Meta(object):
model = Foo
fields = '__all__'
def _ensure_no_blanks(self, field):
value = self.cleaned_data.get(field)
if value is None:
raise forms.ValidationError(_('This field is required.'))
return value
# repeat methods for every field to check
def clean_field1(self):
return self._ensure_no_blanks('field1')
def clean_field2(self):
return self._ensure_no_blanks('field2')
def clean_field3(self):
return self._ensure_no_blanks('field3')
form = FooForm
正如您所看到的,必须编写clean_field1
,clean_field2
和clean_field_n
是重复且容易出错的,所以我编写了这个帮助函数来生成模型管理员:
import functools
from django import forms
def form_with_fields(model_class, required_fields):
class CustomForm(forms.ModelForm):
class Meta(object):
model = model_class
fields = '__all__'
def ensure_no_blanks(self, field):
print field
value = self.cleaned_data.get(field)
if value is None:
raise forms.ValidationError('This field is required.')
return value
# make a clean_bar method for every field that I need to check for None
for field_name in required_fields:
handler = functools.partial(CustomForm.ensure_no_blanks, field=field_name)
setattr(CustomForm, 'clean_' + field_name, lambda self: handler(self))
return CustomForm
class CustomAdmin(ModelAdmin):
form = form_with_fields(Foo, ['field1', 'field2', 'field3'])
但是,如果您运行此类管理员,并且您尝试通过管理员保存Foo
模型,则会在终端中看到print field
三次打印field3
(即所有partial
都保留最后运行的值。)
其他尝试包括覆盖CustomForm
__getattr__()
,并将CustomForm
包裹在type('Form', (CustomForm,), ...
中,这也会表现出相同的行为。
有没有干涸的方法来实现这个目标?
答案 0 :(得分:2)
setattr(CustomForm, 'clean_' + field_name, lambda self: handler(self))
问题是lambda函数。 handler
在lambda之外定义。调用lambda时访问它,而不是在定义时访问它。由于这是在for循环完成之后,所以总是得到使用最后一个字段名的函数。
请参阅Python文档中的this FAQ entry以获得更全面的解释。
在这种情况下,您根本不需要lambda。只需使用handler
本身。
setattr(CustomForm, 'clean_' + field_name, handler)
然而,正如保罗在评论中所暗示的那样,它会很多
def clean(self):
for field in required_fields(self):
self.ensure_no_blanks(field)
您可能需要更改ensure_no_blanks
以使用add_error
,以便将验证错误添加到正确的字段中。
if value is None:
self.add_error(field, 'This field is required.')
另一种选择是为required=True
方法中的字段设置__init__
,然后让表单进行验证。
class CustomForm(forms.ModelForm):
class Meta(object):
model = model_class
fields = '__all__'
def __init__(self, *args, **kwargs):
super(CustomForm, self).__init__(*args, **kwargs)
for field in required_fields:
self.fields[field].required = True