Django

Code

Ticket #6337: ModelFormMetaclass-inheritance.diff

File ModelFormMetaclass-inheritance.diff, 6.9 kB (added by semenov, 8 months ago)
  • django/newforms/models.py

    old new  
    1111from django.core.exceptions import ImproperlyConfigured 
    1212 
    1313from util import ValidationError, ErrorList 
    14 from forms import BaseForm 
     14from forms import BaseForm, get_declared_fields 
    1515from fields import Field, ChoiceField, EMPTY_VALUES 
    1616from widgets import Select, SelectMultiple, MultipleHiddenInput 
    1717 
     
    214214class ModelFormMetaclass(type): 
    215215    def __new__(cls, name, bases, attrs, 
    216216                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)) 
    219217 
    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() 
    227219 
    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 
    248234            for base in bases: 
    249235                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 
    260249        return type.__new__(cls, name, bases, attrs) 
    261250 
    262251class BaseModelForm(BaseForm): 
  • django/newforms/forms.py

    old new  
    2222    name = name[0].upper() + name[1:] 
    2323    return name.replace('_', ' ') 
    2424 
     25def 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 
    2538class DeclarativeFieldsMetaclass(type): 
    2639    """ 
    2740    Metaclass that converts Field attributes to a dictionary called 
    2841    'base_fields', taking into account parent class 'base_fields' as well. 
    2942    """ 
    3043    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) 
    4245        return type.__new__(cls, name, bases, attrs) 
    4346 
    4447class BaseForm(StrAndUnicode): 
  • docs/modelforms.txt

    old new  
    311311    ... 
    312312    ...     class Meta: 
    313313    ...         model = Article 
     314 
     315Form inheritance 
     316---------------- 
     317As with the basic forms, you can extend and reuse forms by inheriting them. 
     318The fields will be combined, but only the most recently declared Meta 
     319definition will be used. If you do not include a Meta definition in a child 
     320class, a Meta definition will be looked in its parent classes. Note that it is 
     321not allowed to have more than one base class with the Meta defition.