Ticket #6337: ModelFormMetaclass-inheritance.diff
File ModelFormMetaclass-inheritance.diff, 6.9 KB (added by , 17 years ago) |
---|
-
django/newforms/models.py
11 11 from django.core.exceptions import ImproperlyConfigured 12 12 13 13 from util import ValidationError, ErrorList 14 from forms import BaseForm 14 from forms import BaseForm, get_declared_fields 15 15 from fields import Field, ChoiceField, EMPTY_VALUES 16 16 from widgets import Select, SelectMultiple, MultipleHiddenInput 17 17 … … 214 214 class ModelFormMetaclass(type): 215 215 def __new__(cls, name, bases, attrs, 216 216 formfield_callback=lambda f: f.formfield()): 217 fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)]218 fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter))219 217 220 # If this class is subclassing another Form, add that Form's fields. 221 # Note that we loop over the bases in *reverse*. This is necessary in 222 # order to preserve the correct order of fields. 223 for base in bases[::-1]: 224 if hasattr(base, 'base_fields'): 225 fields = base.base_fields.items() + fields 226 declared_fields = SortedDict(fields) 218 fields = SortedDict() 227 219 228 opts = ModelFormOptions(attrs.get('Meta', None)) 229 attrs['_meta'] = opts 230 231 # Don't allow more than one Meta model defenition in bases. The fields 232 # would be generated correctly, but the save method won't deal with 233 # more than one object. 234 base_models = [] 235 for base in bases: 236 base_opts = getattr(base, '_meta', None) 237 base_model = getattr(base_opts, 'model', None) 238 if base_model is not None: 239 base_models.append(base_model) 240 if len(base_models) > 1: 241 raise ImproperlyConfigured("%s's base classes define more than one model." % name) 242 243 # If a model is defined, extract form fields from it and add them to base_fields 244 if attrs['_meta'].model is not None: 245 # Don't allow a subclass to define a different Meta model than a 246 # parent class has. Technically the right fields would be generated, 247 # but the save method will not deal with more than one model. 220 meta = attrs.get('Meta', None) 221 if meta is not None: 222 opts = ModelFormOptions(meta) 223 # If a model is defined, extract form fields from it 224 if opts.model is not None: 225 fields = fields_for_model(opts.model, opts.fields, 226 opts.exclude, formfield_callback) 227 else: 228 # No Meta options defined in the class. Search base classes, but 229 # don't allow more than one Meta definition. The fields would be 230 # generated correctly, but the save method won't deal with more 231 # than one object. Also, it wouldn't be clear what to do with 232 # multiple fields and exclude lists. 233 opts = None 248 234 for base in bases: 249 235 base_opts = getattr(base, '_meta', None) 250 base_model = getattr(base_opts, 'model', None) 251 if base_model and base_model is not opts.model: 252 raise ImproperlyConfigured('%s defines a different model than its parent.' % name) 253 model_fields = fields_for_model(opts.model, opts.fields, 254 opts.exclude, formfield_callback) 255 # fields declared in base classes override fields from the model 256 model_fields.update(declared_fields) 257 attrs['base_fields'] = model_fields 258 else: 259 attrs['base_fields'] = declared_fields 236 if base_opts is not None: 237 if opts is not None: 238 raise ImproperlyConfigured("%s's base classes define more than one Meta options." % name) 239 opts = base_opts 240 if opts is None: 241 # No Meta options defined neither in the class nor its base classes 242 opts = ModelFormOptions() 243 244 attrs['_meta'] = opts 245 246 # Fields declared explicitely override fields from the model 247 fields.update(get_declared_fields(bases, attrs)) 248 attrs['base_fields'] = fields 260 249 return type.__new__(cls, name, bases, attrs) 261 250 262 251 class BaseModelForm(BaseForm): -
django/newforms/forms.py
22 22 name = name[0].upper() + name[1:] 23 23 return name.replace('_', ' ') 24 24 25 def get_declared_fields(bases, attrs): 26 fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)] 27 fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter)) 28 29 # If this class is subclassing another Form, add that Form's fields. 30 # Note that we loop over the bases in *reverse*. This is necessary in 31 # order to preserve the correct order of fields. 32 for base in bases[::-1]: 33 if hasattr(base, 'base_fields'): 34 fields = base.base_fields.items() + fields 35 36 return SortedDict(fields) 37 25 38 class DeclarativeFieldsMetaclass(type): 26 39 """ 27 40 Metaclass that converts Field attributes to a dictionary called 28 41 'base_fields', taking into account parent class 'base_fields' as well. 29 42 """ 30 43 def __new__(cls, name, bases, attrs): 31 fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)] 32 fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter)) 33 34 # If this class is subclassing another Form, add that Form's fields. 35 # Note that we loop over the bases in *reverse*. This is necessary in 36 # order to preserve the correct order of fields. 37 for base in bases[::-1]: 38 if hasattr(base, 'base_fields'): 39 fields = base.base_fields.items() + fields 40 41 attrs['base_fields'] = SortedDict(fields) 44 attrs['base_fields'] = get_declared_fields(bases, attrs) 42 45 return type.__new__(cls, name, bases, attrs) 43 46 44 47 class BaseForm(StrAndUnicode): -
docs/modelforms.txt
311 311 ... 312 312 ... class Meta: 313 313 ... model = Article 314 315 Form inheritance 316 ---------------- 317 As with the basic forms, you can extend and reuse forms by inheriting them. 318 The fields will be combined, but only the most recently declared Meta 319 definition will be used. If you do not include a Meta definition in a child 320 class, a Meta definition will be looked in its parent classes. Note that it is 321 not allowed to have more than one base class with the Meta defition.