如果我有两种形式,基于不同的基类(比如Form和ModelForm),但我想在两者中使用几个字段,我可以以干燥的方式重用它们吗?
考虑以下情况:
class AfricanSwallowForm(forms.ModelForm):
airspeed_velocity = forms.IntegerField(some_important_details_here)
is_migratory = forms.BooleanField(more_important_details)
class Meta:
model = AfricanBird
class EuropeanSwallowForm(forms.Form):
airspeed_velocity = forms.IntegerField(some_important_details_here)
is_migratory = forms.BooleanField(more_important_details)
....有没有办法可以重用字段airspeed_velocity和is_migratory?想象一下,我有几十种这样的形式。如果我一遍又一遍地写这些代码,代码将会浸泡。
(假设,为了这个问题的目的,我不能或不会将airspeed_velocity和is_migratory转换为模型AfricanBird的字段。)
答案 0 :(得分:5)
您可以使用多重继承aka mixins 来分解Form和ModelForm中使用的字段。
class SwallowFormFields:
airspeed_velocity = forms.IntegerField( ... )
is_migratory = forms.BooleanField( ... )
class AfricanSwallowForm(forms.ModelForm, SwallowFormFields):
class Meta:
model = AfricanBird
class EuropeanSwallowForm(forms.Form, SwallowFormFields):
pass
<强>更新强>
由于这不适用于Django元编程,您需要创建一个自定义__init__
构造函数,将继承的字段添加到对象的字段列表中,或者您可以在类定义中显式添加引用:
class SwallowFormFields:
airspeed_velocity = forms.IntegerField()
is_migratory = forms.BooleanField()
class AfricanSwallowForm(forms.ModelForm):
airspeed_velocity = SwallowFormFields.airspeed_velocity
is_migratory = SwallowFormFields.is_migratory
class Meta:
model = AfricanSwallow
class EuropeanSwallowForm(forms.Form):
airspeed_velocity = SwallowFormFields.airspeed_velocity
is_migratory = SwallowFormFields.is_migratory
<强>更新强>:
当然,您不必将共享字段嵌套到类中 - 您也可以将它们简单地定义为全局...
airspeed_velocity = forms.IntegerField()
is_migratory = forms.BooleanField()
class AfricanSwallowForm(forms.ModelForm):
airspeed_velocity = airspeed_velocity
is_migratory = is_migratory
class Meta:
model = AfricanSwallow
class EuropeanSwallowForm(forms.Form):
airspeed_velocity = airspeed_velocity
is_migratory = is_migratory
<强>更新强>
好的,如果你真的想干到最大,你必须使用元类。
所以你可以这样做:
from django.forms.models import ModelForm, ModelFormMetaclass
from django.forms.forms import get_declared_fields, DeclarativeFieldsMetaclass
from django.utils.copycompat import deepcopy
class MixinFormMetaclass(ModelFormMetaclass, DeclarativeFieldsMetaclass):
def __new__(cls, name, bases, attrs):
# default __init__ that calls all base classes
def init_all(self, *args, **kwargs):
for base in bases:
super(base, self).__init__(*args, **kwargs)
attrs.setdefault('__init__', init_all)
# collect declared fields
attrs['declared_fields'] = get_declared_fields(bases, attrs, False)
# create the class
new_cls = super(MixinFormMetaclass, cls).__new__(cls, name, bases, attrs)
return new_cls
class MixinForm(object):
__metaclass__ = MixinFormMetaclass
def __init__(self, *args, **kwargs):
self.fields = deepcopy(self.declared_fields)
您现在可以从MixinForm派生您的表单域集合,如下所示:
class SwallowFormFields(MixinForm):
airspeed_velocity = forms.IntegerField()
is_migratory = forms.BooleanField()
class MoreFormFields(MixinForm):
is_endangered = forms.BooleanField()
然后将它们添加到基类列表中,如下所示:
class EuropeanSwallowForm(forms.Form, SwallowFormFields, MoreFormFields):
pass
class AfricanSwallowForm(forms.ModelForm, SwallowFormFields):
class Meta:
model = AfricanSwallow
那它做了什么?
__init__
构造函数,以确保MixinForm的__init__
方法被神奇地调用。 (否则你必须明确地调用它。)MixinForm.__init__
将声明的字段复制到字段属性请注意,我既不是Python大师也不是django开发人员,而且元类很危险。因此,如果您遇到奇怪的行为,请更好地坚持以上更详细的方法:)
祝你好运!
答案 1 :(得分:1)
工厂式方法怎么样?
def form_factory(class_name, base, field_dict):
always_has = {
'airspeed_velocity': forms.IntegerField(some_important_details_here),
'is_migratory': forms.BooleanField(more_important_details)
}
always_has.update(field_dict)
return type(class_name, (base,), always_has)
def meta_factory(form_model):
class Meta:
model = form_model
return Meta
AfricanSwallowForm = form_factory('AfricanSwallowForm', forms.ModelForm, {
'other' = forms.IntegerField(some_important_details_here),
'Meta': meta_factory(AfricanBird),
})
EuropeanSwallowForm = form_factory('EuropeanSwallowForm', forms.Form, {
'and_a_different' = forms.IntegerField(some_important_details_here),
})
就此而言,您可以在此处修改工厂函数以查看现有的表单类并选择所需的属性,这样您就不会丢失声明性语法......
答案 2 :(得分:0)
<击> 撞击>
<击>class SwallowForm(forms.Form):
airspeed_velocity = forms.IntegerField()
is_migratory = forms.BooleanField()
class AfricanSwallowForm(forms.ModelForm, SwallowForm):
class Meta:
model = AfricanSwallow
class EuropeanSwallowForm(forms.Form, SwallowForm):
...
应该有效。
击>
我有一些运行良好的代码,并且字段attr看起来像这样。
languages_field = forms.ModelMultipleChoiceField(
queryset=Language.objects.all(),
widget=forms.CheckboxSelectMultiple,
required=False
)
class FooLanguagesForm(forms.ModelForm):
languages = languages_field
class Meta:
model = Foo
fields = ('languages', )
请注意,我仍然使用Meta中的字段元组。该字段显示在表单实例的字段中,无论它是否在Meta.fields中。
我有一个使用此模式的常规表单:
genres_field = forms.ModelMultipleChoiceField(
queryset=blah,
widget=forms.CheckboxSelectMultiple,
#required=False,
)
class FooGenresForm(forms.Form):
genres = genres_field
我看到我的田地字典正在运作。
In [6]: f = FooLanguagesForm()
In [7]: f.fields
Out[7]: {'languages': <django.forms.models.ModelMultipleChoiceField object at 0x1024be450>}
In [8]: f2 = FooGenresForm()
In [9]: f2.fields
Out[9]: {'genres': <django.forms.models.ModelMultipleChoiceField object at 0x1024be3d0>}
答案 3 :(得分:0)
创建IntegerField的子类
class AirspeedField(forms.IntegerField):
def __init__():
super(AirspeedField, self).__init__(some_important_details_here)
答案 4 :(得分:-1)
我刚刚制作了一个解决这个问题的片段:
https://djangosnippets.org/snippets/10523/
它使用了酥脆的形状,但可以使用相同的想法而没有酥脆的形式。我们的想法是在同一个表单标签下使用多个表单。