diff options
| author | Loic Bistuer <loic.bistuer@sixmedia.com> | 2013-07-21 02:25:27 +0700 |
|---|---|---|
| committer | Loic Bistuer <loic.bistuer@sixmedia.com> | 2013-10-14 21:59:30 +0700 |
| commit | ac5ec7b8bcc5623cc05d4df900c39e194f5affcf (patch) | |
| tree | 63820b413bbd2e988d5bf5af41b53c03602ff3fc /django/forms/forms.py | |
| parent | 54cd930baf49c45e3e91338a9f4a56d19090ff25 (diff) | |
Fixed #19617 -- Refactored Form metaclasses to support more inheritance scenarios.
Thanks apollo13, funkybob and mjtamlyn for the reviews.
Diffstat (limited to 'django/forms/forms.py')
| -rw-r--r-- | django/forms/forms.py | 48 |
1 files changed, 38 insertions, 10 deletions
diff --git a/django/forms/forms.py b/django/forms/forms.py index 490d9c2c3b..8d0fa23daa 100644 --- a/django/forms/forms.py +++ b/django/forms/forms.py @@ -11,7 +11,7 @@ import warnings from django.core.exceptions import ValidationError from django.forms.fields import Field, FileField from django.forms.utils import flatatt, ErrorDict, ErrorList -from django.forms.widgets import Media, media_property, TextInput, Textarea +from django.forms.widgets import Media, MediaDefiningClass, TextInput, Textarea from django.utils.html import conditional_escape, format_html from django.utils.encoding import smart_text, force_text, python_2_unicode_compatible from django.utils.safestring import mark_safe @@ -29,6 +29,7 @@ def pretty_name(name): return '' return name.replace('_', ' ').capitalize() + def get_declared_fields(bases, attrs, with_base_fields=True): """ Create a list of form field instances from the passed in 'attrs', plus any @@ -40,6 +41,13 @@ def get_declared_fields(bases, attrs, with_base_fields=True): used. The distinction is useful in ModelForm subclassing. Also integrates any additional media definitions. """ + + warnings.warn( + "get_declared_fields is deprecated and will be removed in Django 1.9.", + PendingDeprecationWarning, + stacklevel=2, + ) + fields = [(field_name, attrs.pop(field_name)) for field_name, obj in list(six.iteritems(attrs)) if isinstance(obj, Field)] fields.sort(key=lambda x: x[1].creation_counter) @@ -57,19 +65,37 @@ def get_declared_fields(bases, attrs, with_base_fields=True): return OrderedDict(fields) -class DeclarativeFieldsMetaclass(type): + +class DeclarativeFieldsMetaclass(MediaDefiningClass): """ - Metaclass that converts Field attributes to a dictionary called - 'base_fields', taking into account parent class 'base_fields' as well. + Metaclass that collects Fields declared on the base classes. """ - def __new__(cls, name, bases, attrs): - attrs['base_fields'] = get_declared_fields(bases, attrs) - new_class = super(DeclarativeFieldsMetaclass, - cls).__new__(cls, name, bases, attrs) - if 'media' not in attrs: - new_class.media = media_property(new_class) + def __new__(mcs, name, bases, attrs): + # Collect fields from current class. + current_fields = [] + for key, value in list(attrs.items()): + if isinstance(value, Field): + current_fields.append((key, value)) + attrs.pop(key) + current_fields.sort(key=lambda x: x[1].creation_counter) + attrs['declared_fields'] = OrderedDict(current_fields) + + new_class = (super(DeclarativeFieldsMetaclass, mcs) + .__new__(mcs, name, bases, attrs)) + + # Walk through the MRO. + declared_fields = OrderedDict() + for base in reversed(new_class.__mro__): + # Collect fields from base class. + if hasattr(base, 'declared_fields'): + declared_fields.update(base.declared_fields) + + new_class.base_fields = declared_fields + new_class.declared_fields = declared_fields + return new_class + @python_2_unicode_compatible class BaseForm(object): # This is the main implementation of all the Form logic. Note that this @@ -398,6 +424,7 @@ class BaseForm(object): """ return [field for field in self if not field.is_hidden] + class Form(six.with_metaclass(DeclarativeFieldsMetaclass, BaseForm)): "A collection of Fields, plus their associated data." # This is a separate class from BaseForm in order to abstract the way @@ -406,6 +433,7 @@ class Form(six.with_metaclass(DeclarativeFieldsMetaclass, BaseForm)): # to define a form using declarative syntax. # BaseForm itself has no way of designating self.fields. + @python_2_unicode_compatible class BoundField(object): "A Field plus data" |
