Django:重用表单字段而不继承?

jMy*_*les 5 django dry django-forms

如果我有两种形式,基于不同的基类(比如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)
Run Code Online (Sandbox Code Playgroud)

....有没有办法可以重复使用字段airspeed_velocity和is_migratory?想象一下,我有几十种这样的形式.如果我一遍又一遍地写这些代码,代码将会浸泡.

(为了这个问题的目的,假设我不能或不会将airspeed_velocity和is_migratory转换为模型AfricanBird的字段.)

cod*_*aft 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
Run Code Online (Sandbox Code Playgroud)

更新:

由于这不适用于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
Run Code Online (Sandbox Code Playgroud)

更新:

当然,您不必将共享字段嵌套到类中 - 您也可以简单地将它们定义为全局...

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
Run Code Online (Sandbox Code Playgroud)

更新:

好的,如果你真的想干到最大,你必须使用元类.

所以你可以这样做:

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)
Run Code Online (Sandbox Code Playgroud)

您现在可以从MixinForm派生您的表单域集合,如下所示:

class SwallowFormFields(MixinForm):
    airspeed_velocity = forms.IntegerField()
    is_migratory = forms.BooleanField()

class MoreFormFields(MixinForm):
    is_endangered = forms.BooleanField()
Run Code Online (Sandbox Code Playgroud)

然后将它们添加到基类列表中,如下所示:

class EuropeanSwallowForm(forms.Form, SwallowFormFields, MoreFormFields):
    pass

class AfricanSwallowForm(forms.ModelForm, SwallowFormFields):
    class Meta:
        model = AfricanSwallow
Run Code Online (Sandbox Code Playgroud)

那它是做什么的?

  • 元类收集MixinForm中声明的所有字段
  • 然后添加自定义__init__构造函数,以确保__init__MixinForm 的方法被神奇地调用.(否则你必须明确地调用它.)
  • MixinForm.__init__ 将声明的字段复制到field属性中

请注意,我既不是Python大师也不是django开发人员,而且元类很危险.所以,如果你遇到奇怪的行为更好地坚持上面更冗长的方法:)

祝好运!


mes*_*ntz 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),
    })
Run Code Online (Sandbox Code Playgroud)

就此而言,您可以修改此处的工厂函数来查看现有的表单类并挑选出您想要的属性,这样您就不会丢失声明性语法...