Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Patrick Altman's solution is only true with <em>regular</em> forms - if you try this with ModelForm's you'll get stuck either with metaclass conflicts or with some of the fields missing.</p> <p>The simplest and shortest solution I found was in an attachment to Django' <a href="https://code.djangoproject.com/ticket/7018" rel="noreferrer">ticket #7018</a> - thank you, <strong>bear330</strong> :o)</p> <p>You'll need:</p> <pre><code>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__)) </code></pre> <p>Then you define your mixin as a usual ModelForm, but <strong>without declaring Meta</strong>:</p> <pre><code>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 </code></pre> <p>And, finally - the final ModelForm, reusing the features of ModelFormMixin. You should define Meta and all the usual stuff. In the final forms you can just call super(...) when you're overriding methods (see below).</p> <p><strong>NOTE</strong>: The final form <strong>must have ParentsIncludedModelFormMetaclass set as a metaclass</strong></p> <p><strong>NOTE</strong>: The order of classes is important - <strong>put the mixin first</strong>, then the ModelFrom.</p> <pre><code>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 </code></pre> <p>Keep in mind that this only works if both classes are ModelForms. If you try to mix and match Form and ModelFrom with this technique, it's not going to be pretty at all :o) </p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload