Решение Патрика Альтмана верно только для обычных форм. Если вы попробуете это с ModelForm, вы застрянете либо с конфликтами метаклассов, либо с отсутствием некоторых полей.
Самое простое и короткое решение, которое я нашел, было в приложении к тикету №7018 Django - спасибо, bear330 :о)
Вам понадобиться:
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__))
Затем вы определяете свой миксин как обычную 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
И, наконец, финальная ModelForm, повторно использующая возможности ModelFormMixin. Вы должны определить Meta и все обычные вещи. В окончательных формах вы можете просто вызвать super(...) при переопределении методов (см. ниже).
ПРИМЕЧАНИЕ. Окончательная форма должна иметь метакласс ParentsIncludedModelFormMetaclass.
ПРИМЕЧАНИЕ. Порядок классов важен — сначала поместите миксин, а затем 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
Имейте в виду, что это работает, только если оба класса являются ModelForms. Если вы попытаетесь смешивать и сочетать Form и ModelFrom с помощью этой техники, это будет совсем не красиво :о)
person
Boris Chervenkov
schedule
20.05.2012
SpecialForm
основывался наMainForm
, правильным порядком будетSpecialForm(NoteFormMixin, MainForm)
. - person enpenax   schedule 23.07.2014