Loading django/forms/forms.py +43 −10 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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) Loading @@ -57,19 +65,42 @@ 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) # Field shadowing. for attr in base.__dict__.keys(): if attr in declared_fields: declared_fields.pop(attr) 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 Loading Loading @@ -398,6 +429,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 Loading @@ -406,6 +438,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" Loading django/forms/models.py +26 −22 Original line number Diff line number Diff line Loading @@ -10,11 +10,11 @@ import warnings from django.core.exceptions import ValidationError, NON_FIELD_ERRORS, FieldError from django.forms.fields import Field, ChoiceField from django.forms.forms import BaseForm, get_declared_fields from django.forms.forms import DeclarativeFieldsMetaclass, BaseForm from django.forms.formsets import BaseFormSet, formset_factory from django.forms.utils import ErrorList from django.forms.widgets import (SelectMultiple, HiddenInput, MultipleHiddenInput, media_property, CheckboxSelectMultiple) MultipleHiddenInput, CheckboxSelectMultiple) from django.utils.encoding import smart_text, force_text from django.utils import six from django.utils.text import get_text_list, capfirst Loading Loading @@ -61,6 +61,7 @@ def construct_instance(form, instance, fields=None, exclude=None): return instance def save_instance(form, instance, fields=None, fail_message='saved', commit=True, exclude=None, construct=True): """ Loading Loading @@ -138,6 +139,7 @@ def model_to_dict(instance, fields=None, exclude=None): data[f.name] = f.value_from_object(instance) return data def fields_for_model(model, fields=None, exclude=None, widgets=None, formfield_callback=None, localized_fields=None, labels=None, help_texts=None, error_messages=None): Loading Loading @@ -207,6 +209,7 @@ def fields_for_model(model, fields=None, exclude=None, widgets=None, ) return field_dict class ModelFormOptions(object): def __init__(self, options=None): self.model = getattr(options, 'model', None) Loading @@ -219,22 +222,16 @@ class ModelFormOptions(object): self.error_messages = getattr(options, 'error_messages', None) class ModelFormMetaclass(type): def __new__(cls, name, bases, attrs): class ModelFormMetaclass(DeclarativeFieldsMetaclass): def __new__(mcs, name, bases, attrs): formfield_callback = attrs.pop('formfield_callback', None) try: parents = [b for b in bases if issubclass(b, ModelForm)] except NameError: # We are defining ModelForm itself. parents = None declared_fields = get_declared_fields(bases, attrs, False) new_class = super(ModelFormMetaclass, cls).__new__(cls, name, bases, attrs) if not parents: new_class = (super(ModelFormMetaclass, mcs) .__new__(mcs, name, bases, attrs)) if bases == (BaseModelForm,): return new_class if 'media' not in attrs: new_class.media = media_property(new_class) opts = new_class._meta = ModelFormOptions(getattr(new_class, 'Meta', None)) # We check if a string was passed to `fields` or `exclude`, Loading @@ -253,7 +250,6 @@ class ModelFormMetaclass(type): if opts.model: # If a model is defined, extract form fields from it. if opts.fields is None and opts.exclude is None: # This should be some kind of assertion error once deprecation # cycle is complete. Loading @@ -263,7 +259,7 @@ class ModelFormMetaclass(type): DeprecationWarning, stacklevel=2) if opts.fields == ALL_FIELDS: # sentinel for fields_for_model to indicate "get the list of # Sentinel for fields_for_model to indicate "get the list of # fields from the model" opts.fields = None Loading @@ -274,8 +270,8 @@ class ModelFormMetaclass(type): # make sure opts.fields doesn't specify an invalid field none_model_fields = [k for k, v in six.iteritems(fields) if not v] missing_fields = set(none_model_fields) - \ set(declared_fields.keys()) missing_fields = (set(none_model_fields) - set(new_class.declared_fields.keys())) if missing_fields: message = 'Unknown field(s) (%s) specified for %s' message = message % (', '.join(missing_fields), Loading @@ -283,13 +279,15 @@ class ModelFormMetaclass(type): raise FieldError(message) # Override default model fields with any custom declared ones # (plus, include all the other declared fields). fields.update(declared_fields) fields.update(new_class.declared_fields) else: fields = declared_fields new_class.declared_fields = declared_fields fields = new_class.declared_fields new_class.base_fields = fields return new_class class BaseModelForm(BaseForm): def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, initial=None, error_class=ErrorList, label_suffix=None, Loading Loading @@ -438,9 +436,11 @@ class BaseModelForm(BaseForm): save.alters_data = True class ModelForm(six.with_metaclass(ModelFormMetaclass, BaseModelForm)): pass def modelform_factory(model, form=ModelForm, fields=None, exclude=None, formfield_callback=None, widgets=None, localized_fields=None, labels=None, help_texts=None, error_messages=None): Loading Loading @@ -780,6 +780,7 @@ class BaseModelFormSet(BaseFormSet): form.fields[self._pk_field.name] = ModelChoiceField(qs, initial=pk_value, required=False, widget=widget) super(BaseModelFormSet, self).add_fields(form, index) def modelformset_factory(model, form=ModelForm, formfield_callback=None, formset=BaseModelFormSet, extra=1, can_delete=False, can_order=False, max_num=None, fields=None, exclude=None, Loading Loading @@ -1021,6 +1022,7 @@ class InlineForeignKeyField(Field): def _has_changed(self, initial, data): return False class ModelChoiceIterator(object): def __init__(self, field): self.field = field Loading @@ -1047,6 +1049,7 @@ class ModelChoiceIterator(object): def choice(self, obj): return (self.field.prepare_value(obj), self.field.label_from_instance(obj)) class ModelChoiceField(ChoiceField): """A ChoiceField whose choices are a model QuerySet.""" # This class is a subclass of ChoiceField for purity, but it doesn't Loading Loading @@ -1141,6 +1144,7 @@ class ModelChoiceField(ChoiceField): data_value = data if data is not None else '' return force_text(self.prepare_value(initial_value)) != force_text(data_value) class ModelMultipleChoiceField(ModelChoiceField): """A MultipleChoiceField whose choices are a model QuerySet.""" widget = SelectMultiple Loading django/forms/widgets.py +8 −4 Original line number Diff line number Diff line Loading @@ -131,12 +131,16 @@ def media_property(cls): return property(_media) class MediaDefiningClass(type): "Metaclass for classes that can have media definitions" def __new__(cls, name, bases, attrs): new_class = super(MediaDefiningClass, cls).__new__(cls, name, bases, attrs) """ Metaclass for classes that can have media definitions. """ def __new__(mcs, name, bases, attrs): new_class = (super(MediaDefiningClass, mcs) .__new__(mcs, name, bases, attrs)) if 'media' not in attrs: new_class.media = media_property(new_class) return new_class @python_2_unicode_compatible Loading docs/internals/deprecation.txt +2 −0 Original line number Diff line number Diff line Loading @@ -467,6 +467,8 @@ these changes. * The ``use_natural_keys`` argument for ``serializers.serialize()`` will be removed. Use ``use_natural_foreign_keys`` instead. * ``django.forms.get_declared_fields`` will be removed. 2.0 --- Loading docs/ref/forms/api.txt +7 −0 Original line number Diff line number Diff line Loading @@ -854,6 +854,13 @@ classes:: <li>Instrument: <input type="text" name="instrument" /></li> <li>Haircut type: <input type="text" name="haircut_type" /></li> .. versionadded:: 1.7 * It's possible to opt-out from a ``Field`` inherited from a parent class by shadowing it. While any non-``Field`` value works for this purpose, it's recommended to use ``None`` to make it explicit that a field is being nullified. .. _form-prefix: Prefixes for forms Loading Loading
django/forms/forms.py +43 −10 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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) Loading @@ -57,19 +65,42 @@ 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) # Field shadowing. for attr in base.__dict__.keys(): if attr in declared_fields: declared_fields.pop(attr) 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 Loading Loading @@ -398,6 +429,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 Loading @@ -406,6 +438,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" Loading
django/forms/models.py +26 −22 Original line number Diff line number Diff line Loading @@ -10,11 +10,11 @@ import warnings from django.core.exceptions import ValidationError, NON_FIELD_ERRORS, FieldError from django.forms.fields import Field, ChoiceField from django.forms.forms import BaseForm, get_declared_fields from django.forms.forms import DeclarativeFieldsMetaclass, BaseForm from django.forms.formsets import BaseFormSet, formset_factory from django.forms.utils import ErrorList from django.forms.widgets import (SelectMultiple, HiddenInput, MultipleHiddenInput, media_property, CheckboxSelectMultiple) MultipleHiddenInput, CheckboxSelectMultiple) from django.utils.encoding import smart_text, force_text from django.utils import six from django.utils.text import get_text_list, capfirst Loading Loading @@ -61,6 +61,7 @@ def construct_instance(form, instance, fields=None, exclude=None): return instance def save_instance(form, instance, fields=None, fail_message='saved', commit=True, exclude=None, construct=True): """ Loading Loading @@ -138,6 +139,7 @@ def model_to_dict(instance, fields=None, exclude=None): data[f.name] = f.value_from_object(instance) return data def fields_for_model(model, fields=None, exclude=None, widgets=None, formfield_callback=None, localized_fields=None, labels=None, help_texts=None, error_messages=None): Loading Loading @@ -207,6 +209,7 @@ def fields_for_model(model, fields=None, exclude=None, widgets=None, ) return field_dict class ModelFormOptions(object): def __init__(self, options=None): self.model = getattr(options, 'model', None) Loading @@ -219,22 +222,16 @@ class ModelFormOptions(object): self.error_messages = getattr(options, 'error_messages', None) class ModelFormMetaclass(type): def __new__(cls, name, bases, attrs): class ModelFormMetaclass(DeclarativeFieldsMetaclass): def __new__(mcs, name, bases, attrs): formfield_callback = attrs.pop('formfield_callback', None) try: parents = [b for b in bases if issubclass(b, ModelForm)] except NameError: # We are defining ModelForm itself. parents = None declared_fields = get_declared_fields(bases, attrs, False) new_class = super(ModelFormMetaclass, cls).__new__(cls, name, bases, attrs) if not parents: new_class = (super(ModelFormMetaclass, mcs) .__new__(mcs, name, bases, attrs)) if bases == (BaseModelForm,): return new_class if 'media' not in attrs: new_class.media = media_property(new_class) opts = new_class._meta = ModelFormOptions(getattr(new_class, 'Meta', None)) # We check if a string was passed to `fields` or `exclude`, Loading @@ -253,7 +250,6 @@ class ModelFormMetaclass(type): if opts.model: # If a model is defined, extract form fields from it. if opts.fields is None and opts.exclude is None: # This should be some kind of assertion error once deprecation # cycle is complete. Loading @@ -263,7 +259,7 @@ class ModelFormMetaclass(type): DeprecationWarning, stacklevel=2) if opts.fields == ALL_FIELDS: # sentinel for fields_for_model to indicate "get the list of # Sentinel for fields_for_model to indicate "get the list of # fields from the model" opts.fields = None Loading @@ -274,8 +270,8 @@ class ModelFormMetaclass(type): # make sure opts.fields doesn't specify an invalid field none_model_fields = [k for k, v in six.iteritems(fields) if not v] missing_fields = set(none_model_fields) - \ set(declared_fields.keys()) missing_fields = (set(none_model_fields) - set(new_class.declared_fields.keys())) if missing_fields: message = 'Unknown field(s) (%s) specified for %s' message = message % (', '.join(missing_fields), Loading @@ -283,13 +279,15 @@ class ModelFormMetaclass(type): raise FieldError(message) # Override default model fields with any custom declared ones # (plus, include all the other declared fields). fields.update(declared_fields) fields.update(new_class.declared_fields) else: fields = declared_fields new_class.declared_fields = declared_fields fields = new_class.declared_fields new_class.base_fields = fields return new_class class BaseModelForm(BaseForm): def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, initial=None, error_class=ErrorList, label_suffix=None, Loading Loading @@ -438,9 +436,11 @@ class BaseModelForm(BaseForm): save.alters_data = True class ModelForm(six.with_metaclass(ModelFormMetaclass, BaseModelForm)): pass def modelform_factory(model, form=ModelForm, fields=None, exclude=None, formfield_callback=None, widgets=None, localized_fields=None, labels=None, help_texts=None, error_messages=None): Loading Loading @@ -780,6 +780,7 @@ class BaseModelFormSet(BaseFormSet): form.fields[self._pk_field.name] = ModelChoiceField(qs, initial=pk_value, required=False, widget=widget) super(BaseModelFormSet, self).add_fields(form, index) def modelformset_factory(model, form=ModelForm, formfield_callback=None, formset=BaseModelFormSet, extra=1, can_delete=False, can_order=False, max_num=None, fields=None, exclude=None, Loading Loading @@ -1021,6 +1022,7 @@ class InlineForeignKeyField(Field): def _has_changed(self, initial, data): return False class ModelChoiceIterator(object): def __init__(self, field): self.field = field Loading @@ -1047,6 +1049,7 @@ class ModelChoiceIterator(object): def choice(self, obj): return (self.field.prepare_value(obj), self.field.label_from_instance(obj)) class ModelChoiceField(ChoiceField): """A ChoiceField whose choices are a model QuerySet.""" # This class is a subclass of ChoiceField for purity, but it doesn't Loading Loading @@ -1141,6 +1144,7 @@ class ModelChoiceField(ChoiceField): data_value = data if data is not None else '' return force_text(self.prepare_value(initial_value)) != force_text(data_value) class ModelMultipleChoiceField(ModelChoiceField): """A MultipleChoiceField whose choices are a model QuerySet.""" widget = SelectMultiple Loading
django/forms/widgets.py +8 −4 Original line number Diff line number Diff line Loading @@ -131,12 +131,16 @@ def media_property(cls): return property(_media) class MediaDefiningClass(type): "Metaclass for classes that can have media definitions" def __new__(cls, name, bases, attrs): new_class = super(MediaDefiningClass, cls).__new__(cls, name, bases, attrs) """ Metaclass for classes that can have media definitions. """ def __new__(mcs, name, bases, attrs): new_class = (super(MediaDefiningClass, mcs) .__new__(mcs, name, bases, attrs)) if 'media' not in attrs: new_class.media = media_property(new_class) return new_class @python_2_unicode_compatible Loading
docs/internals/deprecation.txt +2 −0 Original line number Diff line number Diff line Loading @@ -467,6 +467,8 @@ these changes. * The ``use_natural_keys`` argument for ``serializers.serialize()`` will be removed. Use ``use_natural_foreign_keys`` instead. * ``django.forms.get_declared_fields`` will be removed. 2.0 --- Loading
docs/ref/forms/api.txt +7 −0 Original line number Diff line number Diff line Loading @@ -854,6 +854,13 @@ classes:: <li>Instrument: <input type="text" name="instrument" /></li> <li>Haircut type: <input type="text" name="haircut_type" /></li> .. versionadded:: 1.7 * It's possible to opt-out from a ``Field`` inherited from a parent class by shadowing it. While any non-``Field`` value works for this purpose, it's recommended to use ``None`` to make it explicit that a field is being nullified. .. _form-prefix: Prefixes for forms Loading