Last active
August 29, 2015 13:56
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- 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