使用带有Django表单类的mixin

Jor*_*ter 22 django mixins django-forms

我正在考虑创建一个mixin表单类,以便我可以将一组通用字段添加到各种非常不同的表单中.仅仅使用它作为基类是行不通的,因为我希望能够使用其他表单作为基类,如下所示:

class NoteFormMixin(object):
    note = forms.CharField()

class MainForm(forms.Form):
    name = forms.CharField()
    age = forms.IntegerField()

class SpecialForm(MainForm, NoteFormMixin):
    favorite_color = forms.CharField()
Run Code Online (Sandbox Code Playgroud)

我唯一的问题是:这是如何工作的?到目前为止看起来如果我使用mixin,那么它无法识别mixin中设置的字段:

>>> ff1 = SpecialForm()
>>> ff1.fields
{'name': <django.forms.fields.CharField object at 0x178d3110>, 'age': <django.forms.fields.IntegerField object at 0x178d3190>, 'favorite_color': <django.forms.fields.CharField object at 0x178d3210>}
Run Code Online (Sandbox Code Playgroud)

这只是无法做到的事情吗?

Pat*_*man 26

问题是你的NoteFormMixin派生自object而不是forms.Form.您需要将其更改为:

class NoteFormMixin(forms.Form):
    note = forms.CharField()
Run Code Online (Sandbox Code Playgroud)

  • 是的,你可以这样做.让你的Mixin派生自对象,并在Mixin的__init__方法中动态添加所有字段.然后,在你的真实表单类'__init__方法中调用YourMixinClass .__ init __(self,*args,**kwargs) (8认同)
  • 似乎@ JordanReiter不再是一个问题.较新版本的Django允许您在mixin中使用`form.Form`作为`form.ModelForm`子类.我现在正在这样做,它完全符合预期. (3认同)
  • 有没有办法这样做,所以我可以在`form.ModelForm`和`form.Form`类中使用mixin? (2认同)

Bor*_*kov 6

Patrick Altman的解决方案仅适用于常规表单 - 如果您使用ModelForm尝试此解决方案,您将遇到元类冲突或缺少某些字段.

我找到的最简单和最简单的解决方案是附加到Django的门票#7018 - 谢谢你,bear330:o)

你需要:

from django.forms.forms import get_declared_fields
. . .

class ParentsIncludedModelFormMetaclass(ModelFormMetaclass):
    """
        Thanks to bear330 - taken from https://code.djangoproject.com/attachment/ticket/7018/metaforms.py
    """

    def __new__(cls, name, bases, attrs):
        # We store attrs as ModelFormMetaclass.__new__ clears all fields from it
        attrs_copy = attrs.copy()
        new_class = super(ParentsIncludedModelFormMetaclass, cls).__new__(cls, name, bases, attrs)
        # All declared fields + model fields from parent classes
        fields_without_current_model = get_declared_fields(bases, attrs_copy, True)
        new_class.base_fields.update(fields_without_current_model)
        return new_class


def get_next_in_mro(current_class, class_to_find):
    """
        Small util - used to call get the next class in the MRO chain of the class
        You'll need this in your Mixins if you want to override a standard ModelForm method
    """
    mro = current_class.__mro__
    try:
        class_index = mro.index(class_to_find)
        return mro[class_index+1]
    except ValueError:
        raise TypeError('Could not find class %s in MRO of class %s' % (class_to_find.__name__, current_class.__name__))
Run Code Online (Sandbox Code Playgroud)

然后你将mixin定义为通常的ModelForm,但是没有声明Meta:

from django import forms
class ModelFormMixin(forms.ModelForm):

    field_in_mixin = forms.CharField(required=True, max_length=100, label=u"Field in mixin")
    . . .

    # if you need special logic in your __init__ override as usual, but make sure to
    # use get_next_in_mro() instead of super()
    def __init__(self, *args, **kwargs):
        #
        result = get_next_in_mro(self.__class__, ModelFormMixin).__init__(self, *args, **kwargs)

        # do your specific initializations - you have access to self.fields and all the usual stuff
        print "ModelFormMixin.__init__"

        return result

    def clean(self):
        result = get_next_in_mro(self.__class__, ModelFormMixin).clean(self)

        # do your specific cleaning
        print "ModelFormMixin.clean"

        return result
Run Code Online (Sandbox Code Playgroud)

最后 - 最终的ModelForm,重用ModelFormMixin的功能.你应该定义Meta和所有常用的东西.在最终形式中,当你重写方法时,你可以调用super(...)(见下文).

注意:最终表单必须将ParentsIncludedModelFormMetaclass设置为元类

注意:类的顺序很重要 - 首先是mixin,然后是ModelFrom.

class FinalModelForm(ModelFormMixin, forms.ModelForm):
    """
        The concrete form.
    """
    __metaclass__ = ParentsIncludedModelFormMetaclass

    class Meta:
        model = SomeModel

    field_in_final_form = forms.CharField(required=True, max_length=100, label=u"Field in final form")

    def clean(self):
        result = super(FinalModelForm, self).clean()

        # do your specific cleaning
        print "FinalModelForm.clean"

        return result
Run Code Online (Sandbox Code Playgroud)

请记住,这只适用于两个类都是ModelForms的情况.如果你试图将Form和ModelFrom与这种技术混合搭配,它就不会很漂亮了:o)