Code

Ticket #5050: newforms_metaclassing.patch

File newforms_metaclassing.patch, 3.3 KB (added by SmileyChris, 7 years ago)
  • django/newforms/forms.py

     
    3333    def copy(self): 
    3434        return SortedDictFromList([(k, copy.copy(v)) for k, v in self.items()]) 
    3535 
    36 class DeclarativeFieldsMetaclass(type): 
     36class BaseFieldsMetaclass(type): 
    3737    """ 
    38     Metaclass that converts Field attributes to a dictionary called 
    39     'base_fields', taking into account parent class 'base_fields' as well. 
     38    Metaclass that ensures attrs will have a dictionary called 'base_fields', 
     39    built on the parent class 'base_fields'. 
    4040    """ 
    4141    def __new__(cls, name, bases, attrs): 
    42         fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)] 
    43         fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter)) 
    44  
    45         # If this class is subclassing another Form, add that Form's fields. 
     42        fields = attrs.get('base_fields', {}).items() 
     43        # If this class is subclassing another BaseForm, add that form's fields. 
    4644        # Note that we loop over the bases in *reverse*. This is necessary in 
    4745        # order to preserve the correct order of fields. 
    4846        for base in bases[::-1]: 
    4947            if hasattr(base, 'base_fields'): 
    5048                fields = base.base_fields.items() + fields 
    51  
    52         attrs['base_fields'] = SortedDictFromList(fields) 
     49        attrs['base_fields'] = SortedDictFromList(fields)         
    5350        return type.__new__(cls, name, bases, attrs) 
    5451 
     52class DeclarativeFieldsMetaclass(BaseFieldsMetaclass): 
     53    """ 
     54    Metaclass that converts Field attributes to the dictionary attribute called 
     55    'base_fields' created by BaseFieldsMetaclass. 
     56    """ 
     57    def __new__(cls, name, bases, attrs): 
     58        fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)] 
     59        fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter))         
     60 
     61        newcls = BaseFieldsMetaclass.__new__(cls, name, bases, attrs) 
     62 
     63        for k, v in fields: 
     64            newcls.base_fields[k] = v 
     65 
     66        return newcls 
     67 
    5568class BaseForm(StrAndUnicode): 
    5669    # This is the main implementation of all the Form logic. Note that this 
    5770    # class is different than Form. See the comments by the Form class for more 
    5871    # information. Any improvements to the form API should be made to *this* 
    5972    # class, not to the Form class. 
     73    __metaclass__ = BaseFieldsMetaclass 
    6074    def __init__(self, data=None, auto_id='id_%s', prefix=None, initial=None): 
    6175        self.is_bound = data is not None 
    6276        self.data = data or {} 
  • tests/modeltests/model_forms/models.py

     
    526526True 
    527527>>> f.cleaned_data 
    528528{'phone': u'312-555-1212', 'description': u'Assistance'} 
     529 
     530 
     531# Multiple subclasses ######################################################### 
     532 
     533>>> WriterForm = form_for_model(Writer) 
     534>>> PhoneNumberForm = form_for_model(PhoneNumber) 
     535>>> class MixinForm(WriterForm, PhoneNumberForm): 
     536...     pass 
     537>>> print MixinForm(auto_id=None).base_fields.keys() 
     538['name', 'phone', 'description'] 
    529539"""}