If you try to use multiple inheritance with a modelform (to mix in some fields from an already existing form class for example) you'll get the following rather terrifying error:
"Error when calling the metaclass bases metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases"
The solution is to first create the ModelForm, then create a NEW class that inherits from both the ModelForm and the form you want to mixin, then finally apply the recipe from here: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/204197
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | from django import newforms as forms
from somewhere import classmaker
class FormToMixin(forms.Form):
question = forms.CharField(max_length=100, required=True)
answer = forms.CharField(max_length = 20, required=True)
class FormFromModel(forms.ModelForm):
class Meta:
model = SomeModel
class CombinedForm(FormToMixin, FormFromModel):
# Magically resolves the metaclass conflict for you
__metaclass__ = classmaker()
# The below code defines classmaker() - you should put this in a separate
# module and import it above your form definitions.
# From http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/204197
import inspect, types, __builtin__
############## preliminary: two utility functions #####################
def skip_redundant(iterable, skipset=None):
"Redundant items are repeated items or items in the original skipset."
if skipset is None: skipset = set()
for item in iterable:
if item not in skipset:
skipset.add(item)
yield item
def remove_redundant(metaclasses):
skipset = set([types.ClassType])
for meta in metaclasses: # determines the metaclasses to be skipped
skipset.update(inspect.getmro(meta)[1:])
return tuple(skip_redundant(metaclasses, skipset))
##################################################################
## now the core of the module: two mutually recursive functions ##
##################################################################
memoized_metaclasses_map = {}
def get_noconflict_metaclass(bases, left_metas, right_metas):
"""Not intended to be used outside of this module, unless you know
what you are doing."""
# make tuple of needed metaclasses in specified priority order
metas = left_metas + tuple(map(type, bases)) + right_metas
needed_metas = remove_redundant(metas)
# return existing confict-solving meta, if any
if needed_metas in memoized_metaclasses_map:
return memoized_metaclasses_map[needed_metas]
# nope: compute, memoize and return needed conflict-solving meta
elif not needed_metas: # wee, a trivial case, happy us
meta = type
elif len(needed_metas) == 1: # another trivial case
meta = needed_metas[0]
# check for recursion, can happen i.e. for Zope ExtensionClasses
elif needed_metas == bases:
raise TypeError("Incompatible root metatypes", needed_metas)
else: # gotta work ...
metaname = '_' + ''.join([m.__name__ for m in needed_metas])
meta = classmaker()(metaname, needed_metas, {})
memoized_metaclasses_map[needed_metas] = meta
return meta
def classmaker(left_metas=(), right_metas=()):
def make_class(name, bases, adict):
metaclass = get_noconflict_metaclass(bases, left_metas, right_metas)
return metaclass(name, bases, adict)
return make_class
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 10 months ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 1 week ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 5 months ago
- Help text hyperlinks by sa2812 1 year, 6 months ago
Comments
I got only the first form to be displayed
#
same here. only 1 form displays?
#
Please login first before commenting.