Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>You could use multiple inheritance aka <em>mixins</em>, to factor out the fields that are used in both Form and ModelForm. </p> <pre><code>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 </code></pre> <p><strong>UPDATE:</strong></p> <p>Since this does not work with Django metaprogramming, you either need to create a custom <code>__init__</code> constructor that adds the inherited fields to the object's fields list or you can add the references explicitly inside the class definition:</p> <pre><code>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 </code></pre> <p><strong>UPDATE</strong>:</p> <p>Of course you don't have to nest your shared fields into a class -- you could also simply define them as globals ...</p> <pre><code>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 </code></pre> <p><strong>UPDATE:</strong></p> <p>Okay, if you really want to DRY to the max, you have to go with the <strong>metaclasses</strong>.</p> <p>So here is how you may do it:</p> <pre><code>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) </code></pre> <p>You can now derive your collections of formfields from MixinForm like this:</p> <pre><code>class SwallowFormFields(MixinForm): airspeed_velocity = forms.IntegerField() is_migratory = forms.BooleanField() class MoreFormFields(MixinForm): is_endangered = forms.BooleanField() </code></pre> <p>Then add them to the list of base classes like this:</p> <pre><code>class EuropeanSwallowForm(forms.Form, SwallowFormFields, MoreFormFields): pass class AfricanSwallowForm(forms.ModelForm, SwallowFormFields): class Meta: model = AfricanSwallow </code></pre> <p>So what does it do?</p> <ul> <li>The metaclass collects all the fields declared in your MixinForm</li> <li>It then adds custom <code>__init__</code> constructors, to make sure that the <code>__init__</code> method of the MixinForm gets magically called. (Otherwise you would have to call it explicitly.)</li> <li><code>MixinForm.__init__</code> copies the declared fields int the field attribute</li> </ul> <p>Please note that I am neither a Python guru nor a django developer, and that metaclasses are dangerous. So if you encounter weird behaviour better stick with the more verbose approach above :)</p> <p>Good Luck!</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