2011-03-03 22 views
5

如果我有两种形式,基于不同的基类(比如Form和ModelForm),但我想在两个中都使用几个字段,我可以以干式方式重用它们吗?Django:重用表单域而不继承?

考虑以下情形:

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的领域。)

回答

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), 
    }) 

对于这个问题,你可以在这里修改工厂函数寻找到一个现有的窗体类,并挑选出你想要的属性,这样你就不会失去的声明语法...

+0

起初,我看着这个并将其解散,认为它很常规,但对于现实世界的使用来说太笨拙了。第二次阅读后,我意识到这是一个很棒的方法。它完美地工作,并且足够可读,并带有一些额外的评论。 – jMyles 2011-03-03 20:32:34

4

您可以使用多重继承又名混入,以计算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 

UPDATE:

因为这不符合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 

UPDATE

的类定义中

当然,你不必巢共享领域成为一类 - 你也可以简单地将它们定义为全局...

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 

UPDATE:

好吧,如果你真的想干最大的,你必须去metaclasses

因此,这里是你如何可以做到这一点:

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) 

现在,您可以派生formfields集合来自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 

那么它是做什么的?

  • 元类收集在你的MixinForm声明的所有字段
  • 然后添加自定义__init__构造,确保了MixinForm的__init__方法被称为神奇。 (否则你必须明确地调用它。)
  • MixinForm.__init__份声明的字段诠释领域属性

请注意,我既不是Python的用户,也不是Django的开发人员,并且元类是危险的。所以如果你遇到奇怪的行为更好地坚持以上更详细的方法:)

祝你好运!

+0

这似乎并不适合我。 – jMyles 2011-03-03 18:39:26

+0

嗯。我想这是由于Django元编程的作用。 – codecraft 2011-03-03 18:41:30

+0

实例AfricanSwallowForm现在具有airspeed_velociy和is_migratory作为字段,但.fields不会显示它们,因此它们不会被迭代。很近! – jMyles 2011-03-03 18:49:46

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中的字段元组。 该字段显示在表单实例的字段dict中,不管它是否在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>} 
+2

我不这么认为 - 这会创建派生自Form和ModelForm的类,这会使用Djangos元类创建混乱 – codecraft 2011-03-03 19:25:35

+1

TypeError:调用元类时发生错误 元类冲突:派生类的元类必须是(非-strict)所有基类的元类的子类 – 2011-03-03 19:27:05

0

创建IntegerField

class AirspeedField(forms.IntegerField): 
    def __init__(): 
     super(AirspeedField, self).__init__(some_important_details_here) 
0

的子类,我刚才提出,在干燥的方式解决此问题的一个片段:

https://djangosnippets.org/snippets/10523/

它使用脆皮形式,但同样的想法可以使用没有脆皮形式。这个想法是在同一个表单标签下使用多个表单。