Skip to content

Instantly share code, notes, and snippets.

@IMDagger
Last active August 29, 2015 13:56
Show Gist options
  • Save IMDagger/9102834 to your computer and use it in GitHub Desktop.
Save IMDagger/9102834 to your computer and use it in GitHub Desktop.
Small extension for Django's ModelForm class which allows to override widgets, fields during an inheritance.
# -*- coding: utf-8 -*-
from django import forms
from django.forms import widgets
class ModelFormStyler(forms.ModelForm.__metaclass__):
'''
Allows to write such kind of forms:
...
class Some(ModelFormStyler):
class Meta:
model = ...
widgets = {
...
'field1': widgets.Textarea(attrs={'rows': 5}),
},
override_fields = {
'field1': (forms.CharField, dict(label=..., ...)),
'field2': (MyCustomField, dict(widget=widgets.TextInput())),
}
... methods here ...
class Some2(Some):
class Meta:
model = ...
widgets = {
...
'field1': widgets.Textarea(attrs={'rows': 100500}),
'field2': widgets.Textarea(attrs={'rows': 100500}),
},
labels = {
'field1': 'another label',
},
override_fields = {
'field2': (MyCustomField2, {}),
}
... methods here again ...
'''
__sections = {
'widgets': 'widget', # section name -> argument name
'labels': 'label',
}
def __new__(cls, name, bases, attrs):
super_new = super(ModelFormStyler, cls).__new__
cls._override_custom_widgets(bases, attrs)
new_class = super_new(cls, name, bases, attrs)
return new_class
@classmethod
def __replacement_for(cls, attrs, bases, fname, opt_name):
try:
# .widgets, .labels and etc
return getattr(attrs['Meta'], opt_name)[fname]
except:
pass
for base in bases:
try:
return getattr(base.Meta, opt_name)[fname]
except:
pass
@classmethod
def __field_descr_for(cls, attrs, bases, fname):
return cls.__replacement_for(cls, attrs, bases, fname, 'field_overrides')
@classmethod
def _override_custom_widgets(cls, bases, attrs):
if 'Meta' in attrs:
entire_dirty = set()
for section in ['field_overrides',] + cls.__sections.keys():
# .field_overrides together with .widgets, .labels
dirty = getattr(attrs['Meta'], section, {})
entire_dirty.update(dirty.iterkeys())
dirty_fields = []
# it's now without duplicates
for fname in entire_dirty:
descr = cls.__field_descr_for(attrs, bases, fname)
if descr is not None:
dirty_fields.append((fname, descr))
# create fields for all dirty descriptors (local and parent)
for fname, (ftype, kwargs) in dirty_fields:
options = dict(kwargs)
# process
for section, arg_name in cls.__sections.iteritems():
arg_value = cls.__replacement_for(attrs, bases, fname, section)
if arg_value is not None:
options[arg_name] = arg_value
attrs[fname] = ftype(**options)
class ControlModelForm(forms.ModelForm):
__metaclass__ = ModelFormStyler
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment