Login

Multiple inheritance of newforms and modelforms

Author:
simon
Posted:
April 12, 2008
Language:
Python
Version:
.96
Tags:
newforms inheritance modelforms multipleinheritance metaclasses metaclass
Score:
6 (after 6 ratings)

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

  1. FieldAccessForm (per-field user access for forms derived from models) by Killarny 6 years, 6 months ago
  2. Form and ModelForm inheritance DRY by evilclay 1 year, 10 months ago
  3. unique validation for ModelForm by whiskybar 7 years, 1 month ago
  4. FieldsetForm by Ciantic 8 years ago
  5. AjaxForm Base Classes by btaylordesign 4 years, 1 month ago

Comments

visik7 (on June 1, 2009):

I got only the first form to be displayed

#

chachra (on April 3, 2011):

same here. only 1 form displays?

#

Please login first before commenting.