--- a/web/lib/django/forms/models.py Wed May 19 17:43:59 2010 +0200
+++ b/web/lib/django/forms/models.py Tue May 25 02:43:45 2010 +0200
@@ -3,43 +3,36 @@
and database field objects.
"""
+from django.db import connections
from django.utils.encoding import smart_unicode, force_unicode
from django.utils.datastructures import SortedDict
from django.utils.text import get_text_list, capfirst
from django.utils.translation import ugettext_lazy as _, ugettext
-from util import ValidationError, ErrorList
-from forms import BaseForm, get_declared_fields, NON_FIELD_ERRORS
-from fields import Field, ChoiceField, IntegerField, EMPTY_VALUES
-from widgets import Select, SelectMultiple, HiddenInput, MultipleHiddenInput
+from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
+from django.core.validators import EMPTY_VALUES
+from util import ErrorList
+from forms import BaseForm, get_declared_fields
+from fields import Field, ChoiceField
+from widgets import SelectMultiple, HiddenInput, MultipleHiddenInput
from widgets import media_property
from formsets import BaseFormSet, formset_factory, DELETION_FIELD_NAME
-try:
- set
-except NameError:
- from sets import Set as set # Python 2.3 fallback
-
__all__ = (
'ModelForm', 'BaseModelForm', 'model_to_dict', 'fields_for_model',
'save_instance', 'form_for_fields', 'ModelChoiceField',
'ModelMultipleChoiceField',
)
-
-def save_instance(form, instance, fields=None, fail_message='saved',
- commit=True, exclude=None):
+def construct_instance(form, instance, fields=None, exclude=None):
"""
- Saves bound Form ``form``'s cleaned_data into model instance ``instance``.
-
- If commit=True, then the changes to ``instance`` will be saved to the
- database. Returns ``instance``.
+ Constructs and returns a model instance from the bound ``form``'s
+ ``cleaned_data``, but does not save the returned instance to the
+ database.
"""
from django.db import models
opts = instance._meta
- if form.errors:
- raise ValueError("The %s could not be %s because the data didn't"
- " validate." % (opts.object_name, fail_message))
+
cleaned_data = form.cleaned_data
file_field_list = []
for f in opts.fields:
@@ -50,10 +43,6 @@
continue
if exclude and f.name in exclude:
continue
- # OneToOneField doesn't allow assignment of None. Guard against that
- # instead of allowing it and throwing an error.
- if isinstance(f, models.OneToOneField) and cleaned_data[f.name] is None:
- continue
# Defer saving file-type fields until after the other fields, so a
# callable upload_to can use the values from other fields.
if isinstance(f, models.FileField):
@@ -64,9 +53,28 @@
for f in file_field_list:
f.save_form_data(instance, cleaned_data[f.name])
+ return instance
+
+def save_instance(form, instance, fields=None, fail_message='saved',
+ commit=True, exclude=None, construct=True):
+ """
+ Saves bound Form ``form``'s cleaned_data into model instance ``instance``.
+
+ If commit=True, then the changes to ``instance`` will be saved to the
+ database. Returns ``instance``.
+
+ If construct=False, assume ``instance`` has already been constructed and
+ just needs to be saved.
+ """
+ if construct:
+ instance = construct_instance(form, instance, fields, exclude)
+ opts = instance._meta
+ if form.errors:
+ raise ValueError("The %s could not be %s because the data didn't"
+ " validate." % (opts.object_name, fail_message))
+
# Wrap up the saving of m2m data as a function.
def save_m2m():
- opts = instance._meta
cleaned_data = form.cleaned_data
for f in opts.many_to_many:
if fields and f.name not in fields:
@@ -119,7 +127,7 @@
the ``fields`` argument.
"""
# avoid a circular import
- from django.db.models.fields.related import ManyToManyField, OneToOneField
+ from django.db.models.fields.related import ManyToManyField
opts = instance._meta
data = {}
for f in opts.fields + opts.many_to_many:
@@ -142,7 +150,7 @@
data[f.name] = f.value_from_object(instance)
return data
-def fields_for_model(model, fields=None, exclude=None, formfield_callback=lambda f: f.formfield()):
+def fields_for_model(model, fields=None, exclude=None, widgets=None, formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)):
"""
Returns a ``SortedDict`` containing form fields for the given model.
@@ -154,6 +162,7 @@
in the ``fields`` argument.
"""
field_list = []
+ ignored = []
opts = model._meta
for f in opts.fields + opts.many_to_many:
if not f.editable:
@@ -162,12 +171,21 @@
continue
if exclude and f.name in exclude:
continue
- formfield = formfield_callback(f)
+ if widgets and f.name in widgets:
+ kwargs = {'widget': widgets[f.name]}
+ else:
+ kwargs = {}
+ formfield = formfield_callback(f, **kwargs)
if formfield:
field_list.append((f.name, formfield))
+ else:
+ ignored.append(f.name)
field_dict = SortedDict(field_list)
if fields:
- field_dict = SortedDict([(f, field_dict.get(f)) for f in fields if (not exclude) or (exclude and f not in exclude)])
+ field_dict = SortedDict(
+ [(f, field_dict.get(f)) for f in fields
+ if ((not exclude) or (exclude and f not in exclude)) and (f not in ignored)]
+ )
return field_dict
class ModelFormOptions(object):
@@ -175,12 +193,13 @@
self.model = getattr(options, 'model', None)
self.fields = getattr(options, 'fields', None)
self.exclude = getattr(options, 'exclude', None)
+ self.widgets = getattr(options, 'widgets', None)
class ModelFormMetaclass(type):
def __new__(cls, name, bases, attrs):
formfield_callback = attrs.pop('formfield_callback',
- lambda f: f.formfield())
+ lambda f, **kwargs: f.formfield(**kwargs))
try:
parents = [b for b in bases if issubclass(b, ModelForm)]
except NameError:
@@ -198,7 +217,7 @@
if opts.model:
# If a model is defined, extract form fields from it.
fields = fields_for_model(opts.model, opts.fields,
- opts.exclude, formfield_callback)
+ opts.exclude, opts.widgets, formfield_callback)
# Override default model fields with any custom declared ones
# (plus, include all the other declared fields).
fields.update(declared_fields)
@@ -214,182 +233,127 @@
empty_permitted=False, instance=None):
opts = self._meta
if instance is None:
+ if opts.model is None:
+ raise ValueError('ModelForm has no model class specified.')
# if we didn't get an instance, instantiate a new one
self.instance = opts.model()
object_data = {}
+ self.instance._adding = True
else:
self.instance = instance
+ self.instance._adding = False
object_data = model_to_dict(instance, opts.fields, opts.exclude)
# if initial was provided, it should override the values from instance
if initial is not None:
object_data.update(initial)
+ # self._validate_unique will be set to True by BaseModelForm.clean().
+ # It is False by default so overriding self.clean() and failing to call
+ # super will stop validate_unique from being called.
+ self._validate_unique = False
super(BaseModelForm, self).__init__(data, files, auto_id, prefix, object_data,
error_class, label_suffix, empty_permitted)
+ def _update_errors(self, message_dict):
+ for k, v in message_dict.items():
+ if k != NON_FIELD_ERRORS:
+ self._errors.setdefault(k, self.error_class()).extend(v)
+ # Remove the data from the cleaned_data dict since it was invalid
+ if k in self.cleaned_data:
+ del self.cleaned_data[k]
+ if NON_FIELD_ERRORS in message_dict:
+ messages = message_dict[NON_FIELD_ERRORS]
+ self._errors.setdefault(NON_FIELD_ERRORS, self.error_class()).extend(messages)
+
+ def _get_validation_exclusions(self):
+ """
+ For backwards-compatibility, several types of fields need to be
+ excluded from model validation. See the following tickets for
+ details: #12507, #12521, #12553
+ """
+ exclude = []
+ # Build up a list of fields that should be excluded from model field
+ # validation and unique checks.
+ for f in self.instance._meta.fields:
+ field = f.name
+ # Exclude fields that aren't on the form. The developer may be
+ # adding these values to the model after form validation.
+ if field not in self.fields:
+ exclude.append(f.name)
+
+ # Don't perform model validation on fields that were defined
+ # manually on the form and excluded via the ModelForm's Meta
+ # class. See #12901.
+ elif self._meta.fields and field not in self._meta.fields:
+ exclude.append(f.name)
+ elif self._meta.exclude and field in self._meta.exclude:
+ exclude.append(f.name)
+
+ # Exclude fields that failed form validation. There's no need for
+ # the model fields to validate them as well.
+ elif field in self._errors.keys():
+ exclude.append(f.name)
+
+ # Exclude empty fields that are not required by the form, if the
+ # underlying model field is required. This keeps the model field
+ # from raising a required error. Note: don't exclude the field from
+ # validaton if the model field allows blanks. If it does, the blank
+ # value may be included in a unique check, so cannot be excluded
+ # from validation.
+ else:
+ form_field = self.fields[field]
+ field_value = self.cleaned_data.get(field, None)
+ if not f.blank and not form_field.required and field_value in EMPTY_VALUES:
+ exclude.append(f.name)
+ return exclude
+
def clean(self):
- self.validate_unique()
+ self._validate_unique = True
return self.cleaned_data
+ def _post_clean(self):
+ opts = self._meta
+ # Update the model instance with self.cleaned_data.
+ self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude)
+
+ exclude = self._get_validation_exclusions()
+
+ # Foreign Keys being used to represent inline relationships
+ # are excluded from basic field value validation. This is for two
+ # reasons: firstly, the value may not be supplied (#12507; the
+ # case of providing new values to the admin); secondly the
+ # object being referred to may not yet fully exist (#12749).
+ # However, these fields *must* be included in uniqueness checks,
+ # so this can't be part of _get_validation_exclusions().
+ for f_name, field in self.fields.items():
+ if isinstance(field, InlineForeignKeyField):
+ exclude.append(f_name)
+
+ # Clean the model instance's fields.
+ try:
+ self.instance.clean_fields(exclude=exclude)
+ except ValidationError, e:
+ self._update_errors(e.message_dict)
+
+ # Call the model instance's clean method.
+ try:
+ self.instance.clean()
+ except ValidationError, e:
+ self._update_errors({NON_FIELD_ERRORS: e.messages})
+
+ # Validate uniqueness if needed.
+ if self._validate_unique:
+ self.validate_unique()
+
def validate_unique(self):
- unique_checks, date_checks = self._get_unique_checks()
- form_errors = []
- bad_fields = set()
-
- field_errors, global_errors = self._perform_unique_checks(unique_checks)
- bad_fields.union(field_errors)
- form_errors.extend(global_errors)
-
- field_errors, global_errors = self._perform_date_checks(date_checks)
- bad_fields.union(field_errors)
- form_errors.extend(global_errors)
-
- for field_name in bad_fields:
- del self.cleaned_data[field_name]
- if form_errors:
- # Raise the unique together errors since they are considered
- # form-wide.
- raise ValidationError(form_errors)
-
- def _get_unique_checks(self):
- from django.db.models.fields import FieldDoesNotExist, Field as ModelField
-
- # Gather a list of checks to perform. We only perform unique checks
- # for fields present and not None in cleaned_data. Since this is a
- # ModelForm, some fields may have been excluded; we can't perform a unique
- # check on a form that is missing fields involved in that check. It also does
- # not make sense to check data that didn't validate, and since NULL does not
- # equal NULL in SQL we should not do any unique checking for NULL values.
- unique_checks = []
- # these are checks for the unique_for_<date/year/month>
- date_checks = []
- for check in self.instance._meta.unique_together[:]:
- fields_on_form = [field for field in check if self.cleaned_data.get(field) is not None]
- if len(fields_on_form) == len(check):
- unique_checks.append(check)
-
- # Gather a list of checks for fields declared as unique and add them to
- # the list of checks. Again, skip empty fields and any that did not validate.
- for name in self.fields:
- try:
- f = self.instance._meta.get_field_by_name(name)[0]
- except FieldDoesNotExist:
- # This is an extra field that's not on the ModelForm, ignore it
- continue
- if not isinstance(f, ModelField):
- # This is an extra field that happens to have a name that matches,
- # for example, a related object accessor for this model. So
- # get_field_by_name found it, but it is not a Field so do not proceed
- # to use it as if it were.
- continue
- if self.cleaned_data.get(name) is None:
- continue
- if f.unique:
- unique_checks.append((name,))
- if f.unique_for_date and self.cleaned_data.get(f.unique_for_date) is not None:
- date_checks.append(('date', name, f.unique_for_date))
- if f.unique_for_year and self.cleaned_data.get(f.unique_for_year) is not None:
- date_checks.append(('year', name, f.unique_for_year))
- if f.unique_for_month and self.cleaned_data.get(f.unique_for_month) is not None:
- date_checks.append(('month', name, f.unique_for_month))
- return unique_checks, date_checks
-
-
- def _perform_unique_checks(self, unique_checks):
- bad_fields = set()
- form_errors = []
-
- for unique_check in unique_checks:
- # Try to look up an existing object with the same values as this
- # object's values for all the unique field.
-
- lookup_kwargs = {}
- for field_name in unique_check:
- lookup_value = self.cleaned_data[field_name]
- # ModelChoiceField will return an object instance rather than
- # a raw primary key value, so convert it to a pk value before
- # using it in a lookup.
- if isinstance(self.fields[field_name], ModelChoiceField):
- lookup_value = lookup_value.pk
- lookup_kwargs[str(field_name)] = lookup_value
-
- qs = self.instance.__class__._default_manager.filter(**lookup_kwargs)
-
- # Exclude the current object from the query if we are editing an
- # instance (as opposed to creating a new one)
- if self.instance.pk is not None:
- qs = qs.exclude(pk=self.instance.pk)
-
- # This cute trick with extra/values is the most efficient way to
- # tell if a particular query returns any results.
- if qs.extra(select={'a': 1}).values('a').order_by():
- if len(unique_check) == 1:
- self._errors[unique_check[0]] = ErrorList([self.unique_error_message(unique_check)])
- else:
- form_errors.append(self.unique_error_message(unique_check))
-
- # Mark these fields as needing to be removed from cleaned data
- # later.
- for field_name in unique_check:
- bad_fields.add(field_name)
- return bad_fields, form_errors
-
- def _perform_date_checks(self, date_checks):
- bad_fields = set()
- for lookup_type, field, unique_for in date_checks:
- lookup_kwargs = {}
- # there's a ticket to add a date lookup, we can remove this special
- # case if that makes it's way in
- if lookup_type == 'date':
- date = self.cleaned_data[unique_for]
- lookup_kwargs['%s__day' % unique_for] = date.day
- lookup_kwargs['%s__month' % unique_for] = date.month
- lookup_kwargs['%s__year' % unique_for] = date.year
- else:
- lookup_kwargs['%s__%s' % (unique_for, lookup_type)] = getattr(self.cleaned_data[unique_for], lookup_type)
- lookup_kwargs[field] = self.cleaned_data[field]
-
- qs = self.instance.__class__._default_manager.filter(**lookup_kwargs)
- # Exclude the current object from the query if we are editing an
- # instance (as opposed to creating a new one)
- if self.instance.pk is not None:
- qs = qs.exclude(pk=self.instance.pk)
-
- # This cute trick with extra/values is the most efficient way to
- # tell if a particular query returns any results.
- if qs.extra(select={'a': 1}).values('a').order_by():
- self._errors[field] = ErrorList([
- self.date_error_message(lookup_type, field, unique_for)
- ])
- bad_fields.add(field)
- return bad_fields, []
-
- def date_error_message(self, lookup_type, field, unique_for):
- return _(u"%(field_name)s must be unique for %(date_field)s %(lookup)s.") % {
- 'field_name': unicode(self.fields[field].label),
- 'date_field': unicode(self.fields[unique_for].label),
- 'lookup': lookup_type,
- }
-
- def unique_error_message(self, unique_check):
- model_name = capfirst(self.instance._meta.verbose_name)
-
- # A unique field
- if len(unique_check) == 1:
- field_name = unique_check[0]
- field_label = self.fields[field_name].label
- # Insert the error into the error dict, very sneaky
- return _(u"%(model_name)s with this %(field_label)s already exists.") % {
- 'model_name': unicode(model_name),
- 'field_label': unicode(field_label)
- }
- # unique_together
- else:
- field_labels = [self.fields[field_name].label for field_name in unique_check]
- field_labels = get_text_list(field_labels, _('and'))
- return _(u"%(model_name)s with this %(field_label)s already exists.") % {
- 'model_name': unicode(model_name),
- 'field_label': unicode(field_labels)
- }
+ """
+ Calls the instance's validate_unique() method and updates the form's
+ validation errors if any were raised.
+ """
+ exclude = self._get_validation_exclusions()
+ try:
+ self.instance.validate_unique(exclude=exclude)
+ except ValidationError, e:
+ self._update_errors(e.message_dict)
def save(self, commit=True):
"""
@@ -404,7 +368,7 @@
else:
fail_message = 'changed'
return save_instance(self, self.instance, self._meta.fields,
- fail_message, commit, exclude=self._meta.exclude)
+ fail_message, commit, construct=False)
save.alters_data = True
@@ -474,7 +438,8 @@
pk_key = "%s-%s" % (self.add_prefix(i), self.model._meta.pk.name)
pk = self.data[pk_key]
pk_field = self.model._meta.pk
- pk = pk_field.get_db_prep_lookup('exact', pk)
+ pk = pk_field.get_db_prep_lookup('exact', pk,
+ connection=connections[self.get_queryset().db])
if isinstance(pk, list):
pk = pk[0]
kwargs['instance'] = self._existing_object(pk)
@@ -495,10 +460,10 @@
if not qs.ordered:
qs = qs.order_by(self.model._meta.pk.name)
- if self.max_num > 0:
- self._queryset = qs[:self.max_num]
- else:
- self._queryset = qs
+ # Removed queryset limiting here. As per discussion re: #13023
+ # on django-dev, max_num should not prevent existing
+ # related objects/inlines from being displayed.
+ self._queryset = qs
return self._queryset
def save_new(self, form, commit=True):
@@ -525,17 +490,20 @@
self.validate_unique()
def validate_unique(self):
- # Iterate over the forms so that we can find one with potentially valid
- # data from which to extract the error checks
+ # Collect unique_checks and date_checks to run from all the forms.
+ all_unique_checks = set()
+ all_date_checks = set()
for form in self.forms:
- if hasattr(form, 'cleaned_data'):
- break
- else:
- return
- unique_checks, date_checks = form._get_unique_checks()
+ if not hasattr(form, 'cleaned_data'):
+ continue
+ exclude = form._get_validation_exclusions()
+ unique_checks, date_checks = form.instance._get_unique_checks(exclude=exclude)
+ all_unique_checks = all_unique_checks.union(set(unique_checks))
+ all_date_checks = all_date_checks.union(set(date_checks))
+
errors = []
# Do each of the unique checks (unique and unique_together)
- for unique_check in unique_checks:
+ for uclass, unique_check in all_unique_checks:
seen_data = set()
for form in self.forms:
# if the form doesn't have cleaned_data then we ignore it,
@@ -557,9 +525,9 @@
# mark the data as seen
seen_data.add(row_data)
# iterate over each of the date checks now
- for date_check in date_checks:
+ for date_check in all_date_checks:
seen_data = set()
- lookup, field, unique_for = date_check
+ uclass, lookup, field, unique_for = date_check
for form in self.forms:
# if the form doesn't have cleaned_data then we ignore it,
# it's already invalid
@@ -603,9 +571,9 @@
def get_date_error_message(self, date_check):
return ugettext("Please correct the duplicate data for %(field_name)s "
"which must be unique for the %(lookup)s in %(date_field)s.") % {
- 'field_name': date_check[1],
- 'date_field': date_check[2],
- 'lookup': unicode(date_check[0]),
+ 'field_name': date_check[2],
+ 'date_field': date_check[3],
+ 'lookup': unicode(date_check[1]),
}
def get_form_error(self):
@@ -676,20 +644,24 @@
pk_value = form.instance.pk
else:
try:
- pk_value = self.get_queryset()[index].pk
+ if index is not None:
+ pk_value = self.get_queryset()[index].pk
+ else:
+ pk_value = None
except IndexError:
pk_value = None
if isinstance(pk, OneToOneField) or isinstance(pk, ForeignKey):
qs = pk.rel.to._default_manager.get_query_set()
else:
qs = self.model._default_manager.get_query_set()
+ qs = qs.using(form.instance._state.db)
form.fields[self._pk_field.name] = ModelChoiceField(qs, initial=pk_value, required=False, widget=HiddenInput)
super(BaseModelFormSet, self).add_fields(form, index)
def modelformset_factory(model, form=ModelForm, formfield_callback=lambda f: f.formfield(),
formset=BaseModelFormSet,
extra=1, can_delete=False, can_order=False,
- max_num=0, fields=None, exclude=None):
+ max_num=None, fields=None, exclude=None):
"""
Returns a FormSet class for the given Django model class.
"""
@@ -706,10 +678,10 @@
class BaseInlineFormSet(BaseModelFormSet):
"""A formset for child objects related to a parent."""
def __init__(self, data=None, files=None, instance=None,
- save_as_new=False, prefix=None):
+ save_as_new=False, prefix=None, queryset=None):
from django.db.models.fields.related import RelatedObject
if instance is None:
- self.instance = self.model()
+ self.instance = self.fk.rel.to()
else:
self.instance = instance
self.save_as_new = save_as_new
@@ -719,7 +691,9 @@
backlink_value = self.instance
else:
backlink_value = getattr(self.instance, self.fk.rel.field_name)
- qs = self.model._default_manager.filter(**{self.fk.name: backlink_value})
+ if queryset is None:
+ queryset = self.model._default_manager
+ qs = queryset.filter(**{self.fk.name: backlink_value})
super(BaseInlineFormSet, self).__init__(data, files, prefix=prefix,
queryset=qs)
@@ -742,12 +716,15 @@
# Remove the foreign key from the form's data
form.data[form.add_prefix(self.fk.name)] = None
+
+ # Set the fk value here so that the form can do it's validation.
+ setattr(form.instance, self.fk.get_attname(), self.instance.pk)
return form
#@classmethod
def get_default_prefix(cls):
from django.db.models.fields.related import RelatedObject
- return RelatedObject(cls.fk.rel.to, cls.model, cls.fk).get_accessor_name()
+ return RelatedObject(cls.fk.rel.to, cls.model, cls.fk).get_accessor_name().replace('+','')
get_default_prefix = classmethod(get_default_prefix)
def save_new(self, form, commit=True):
@@ -766,21 +743,32 @@
def add_fields(self, form, index):
super(BaseInlineFormSet, self).add_fields(form, index)
if self._pk_field == self.fk:
- form.fields[self._pk_field.name] = InlineForeignKeyField(self.instance, pk_field=True)
+ name = self._pk_field.name
+ kwargs = {'pk_field': True}
else:
# The foreign key field might not be on the form, so we poke at the
# Model field to get the label, since we need that for error messages.
+ name = self.fk.name
kwargs = {
- 'label': getattr(form.fields.get(self.fk.name), 'label', capfirst(self.fk.verbose_name))
+ 'label': getattr(form.fields.get(name), 'label', capfirst(self.fk.verbose_name))
}
if self.fk.rel.field_name != self.fk.rel.to._meta.pk.name:
kwargs['to_field'] = self.fk.rel.field_name
- form.fields[self.fk.name] = InlineForeignKeyField(self.instance, **kwargs)
+
+ form.fields[name] = InlineForeignKeyField(self.instance, **kwargs)
+
+ # Add the generated field to form._meta.fields if it's defined to make
+ # sure validation isn't skipped on that field.
+ if form._meta.fields:
+ if isinstance(form._meta.fields, tuple):
+ form._meta.fields = list(form._meta.fields)
+ form._meta.fields.append(self.fk.name)
def get_unique_error_message(self, unique_check):
unique_check = [field for field in unique_check if field != self.fk.name]
return super(BaseInlineFormSet, self).get_unique_error_message(unique_check)
+
def _get_foreign_key(parent_model, model, fk_name=None, can_fail=False):
"""
Finds and returns the ForeignKey from model to parent if there is one
@@ -824,7 +812,7 @@
def inlineformset_factory(parent_model, model, form=ModelForm,
formset=BaseInlineFormSet, fk_name=None,
fields=None, exclude=None,
- extra=3, can_order=False, can_delete=True, max_num=0,
+ extra=3, can_order=False, can_delete=True, max_num=None,
formfield_callback=lambda f: f.formfield()):
"""
Returns an ``InlineFormSet`` for the given kwargs.
@@ -914,6 +902,9 @@
for obj in self.queryset.all():
yield self.choice(obj)
+ def __len__(self):
+ return len(self.queryset)
+
def choice(self, obj):
if self.field.to_field_name:
key = obj.serializable_value(self.field.to_field_name)
@@ -948,6 +939,12 @@
self.choice_cache = None
self.to_field_name = to_field_name
+ def __deepcopy__(self, memo):
+ result = super(ChoiceField, self).__deepcopy__(memo)
+ # Need to force a new ModelChoiceIterator to be created, bug #11183
+ result.queryset = result.queryset
+ return result
+
def _get_queryset(self):
return self._queryset
@@ -984,8 +981,7 @@
choices = property(_get_choices, ChoiceField._set_choices)
- def clean(self, value):
- Field.clean(self, value)
+ def to_python(self, value):
if value in EMPTY_VALUES:
return None
try:
@@ -995,6 +991,9 @@
raise ValidationError(self.error_messages['invalid_choice'])
return value
+ def validate(self, value):
+ return Field.validate(self, value)
+
class ModelMultipleChoiceField(ModelChoiceField):
"""A MultipleChoiceField whose choices are a model QuerySet."""
widget = SelectMultiple