Ticket #6845: 6845-against-9226.diff

File 6845-against-9226.diff, 37.2 KB (added by Honza Král, 16 years ago)
  • django/contrib/contenttypes/generic.py

    diff --git a/django/contrib/contenttypes/generic.py b/django/contrib/contenttypes/generic.py
    index 0504592..375f82d 100644
    a b class BaseGenericInlineFormSet(BaseModelFormSet):  
    313313            self.ct_fk_field.name: self.instance.pk,
    314314        })
    315315
    316     def save_new(self, form, commit=True):
     316    def _construct_form(self, i, **kwargs):
    317317        # Avoid a circular import.
    318318        from django.contrib.contenttypes.models import ContentType
    319         kwargs = {
    320             self.ct_field.get_attname(): ContentType.objects.get_for_model(self.instance).pk,
    321             self.ct_fk_field.get_attname(): self.instance.pk,
    322         }
     319        form = super(BaseGenericInlineFormSet, self)._construct_form(i, **kwargs)
     320        if self.save_as_new:
     321            # Remove the key from the form's data, we are only
     322            # creating new instances
     323            form.data[form.add_prefix(self.ct_fk_field.name)] = None
     324            form.data[form.add_prefix(self.ct_field.name)] = None
     325
     326        # set the GenericFK value here so that the form can do it's validation
     327        setattr(form.instance, self.ct_fk_field.attname, self.instance.pk)
     328        setattr(form.instance, self.ct_field.attname, ContentType.objects.get_for_model(self.instance).pk)
     329        return form
     330
     331    def save_new(self, form, commit=True):
    323332        new_obj = self.model(**kwargs)
    324333        return save_instance(form, new_obj, commit=commit)
    325334
  • django/contrib/localflavor/au/forms.py

    diff --git a/django/contrib/localflavor/au/forms.py b/django/contrib/localflavor/au/forms.py
    index afc3a0c..4e8a204 100644
    a b Australian-specific Form helpers  
    44
    55from django.forms import ValidationError
    66from django.forms.fields import Field, RegexField, Select, EMPTY_VALUES
    7 from django.forms.util import smart_unicode
     7from django.utils.encoding import smart_unicode
    88from django.utils.translation import ugettext_lazy as _
    99import re
    1010
  • django/contrib/localflavor/ca/forms.py

    diff --git a/django/contrib/localflavor/ca/forms.py b/django/contrib/localflavor/ca/forms.py
    index 327d938..9544268 100644
    a b Canada-specific Form helpers  
    44
    55from django.forms import ValidationError
    66from django.forms.fields import Field, RegexField, Select, EMPTY_VALUES
    7 from django.forms.util import smart_unicode
     7from django.utils.encoding import smart_unicode
    88from django.utils.translation import ugettext_lazy as _
    99import re
    1010
  • django/core/exceptions.py

    diff --git a/django/core/exceptions.py b/django/core/exceptions.py
    index 1c21031..c0bcbae 100644
    a b class FieldError(Exception):  
    3232    """Some kind of problem with a model field."""
    3333    pass
    3434
     35NON_FIELD_ERRORS = '__all__'
    3536class ValidationError(Exception):
    3637    """An error while validating data."""
    37     pass
     38    def __init__(self, message):
     39        import operator
     40        from django.utils.encoding import force_unicode
     41        """
     42        ValidationError can be passed any object that can be printed (usually
     43        a string), a list of objects or a dictionary.
     44        """
     45        if isinstance(message, dict):
     46            self.message_dict = message
     47            message = reduce(operator.add, message.values())
     48
     49        if isinstance(message, list):
     50            self.messages = [force_unicode(msg) for msg in message]
     51        else:
     52            message = force_unicode(message)
     53            self.messages = [message]
     54
     55
     56
     57    def __str__(self):
     58        # This is needed because, without a __str__(), printing an exception
     59        # instance would result in this:
     60        # AttributeError: ValidationError instance has no attribute 'args'
     61        # See http://www.python.org/doc/current/tut/node10.html#handling
     62        if hasattr(self, 'message_dict'):
     63            return repr(self.message_dict)
     64        return repr(self.messages)
  • django/db/models/base.py

    diff --git a/django/db/models/base.py b/django/db/models/base.py
    index f94d25c..d8558f0 100644
    a b except NameError:  
    99    from sets import Set as set     # Python 2.3 fallback.
    1010
    1111import django.db.models.manager     # Imported to register signal handler.
    12 from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, FieldError
     12from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, FieldError, ValidationError, NON_FIELD_ERRORS
    1313from django.db.models.fields import AutoField
    1414from django.db.models.fields.related import OneToOneRel, ManyToOneRel, OneToOneField
    1515from django.db.models.query import delete_objects, Q, CollectedObjects
    from django.db.models import signals  
    1919from django.db.models.loading import register_models, get_model
    2020from django.utils.functional import curry
    2121from django.utils.encoding import smart_str, force_unicode, smart_unicode
     22from django.utils.text import get_text_list, capfirst
     23from django.utils.translation import ugettext_lazy as _
    2224from django.conf import settings
    2325
    2426
    class Model(object):  
    477479            setattr(self, cachename, obj)
    478480        return getattr(self, cachename)
    479481
     482    def validate_unique(self):
     483        # Gather a list of checks to perform.
     484        unique_checks = self._meta.unique_together[:]
     485
     486        errors = {}
     487       
     488        # Gather a list of checks for fields declared as unique and add them to
     489        # the list of checks.
     490        for f in self._meta.fields:
     491            # MySQL can't handle ... WHERE pk IS NULL, so make sure we
     492            # don't generate queries of that form.
     493            is_null_pk = f.primary_key and self.pk is None
     494            if f.unique and not is_null_pk:
     495                unique_checks.append((f.name,))
     496               
     497        # FIXME: Don't run unique checks on fields that already have an error.
     498        # unique_checks = [check for check in unique_checks if not [x in self._errors for x in check if x in self._errors]]
     499       
     500        for unique_check in unique_checks:
     501            # Try to look up an existing object with the same values as this
     502            # object's values for all the unique field.
     503           
     504            lookup_kwargs = {}
     505            for field_name in unique_check:
     506                f = self._meta.get_field(field_name)
     507                lookup_kwargs[field_name] = getattr(self, f.attname)
     508           
     509            qs = self.__class__._default_manager.filter(**lookup_kwargs)
     510
     511            # Exclude the current object from the query if we are validating an
     512            # already existing instance (as opposed to a new one)
     513            if self.pk is not None:
     514                qs = qs.exclude(pk=self.pk)
     515               
     516            # This cute trick with extra/values is the most efficient way to
     517            # tell if a particular query returns any results.
     518            if qs.extra(select={'a': 1}).values('a').order_by():
     519                model_name = capfirst(self._meta.verbose_name)
     520               
     521                # A unique field
     522                if len(unique_check) == 1:
     523                    field_name = unique_check[0]
     524                    field_label = capfirst(self._meta.get_field(field_name).verbose_name)
     525                    # Insert the error into the error dict, very sneaky
     526                    errors[field_name] = [
     527                        _(u"%(model_name)s with this %(field_label)s already exists.") % \
     528                        {'model_name': unicode(model_name),
     529                         'field_label': unicode(field_label)}
     530                    ]
     531                # unique_together
     532                else:
     533                    field_labels = [capfirst(self._meta.get_field(field_name).verbose_name) for field_name in unique_check]
     534                    field_labels = get_text_list(field_labels, _('and'))
     535                    errors.setdefault(NON_FIELD_ERRORS, []).append(
     536                        _(u"%(model_name)s with this %(field_label)s already exists.") % \
     537                        {'model_name': unicode(model_name),
     538                         'field_label': unicode(field_labels)}
     539                    )
     540               
     541        if errors:
     542            # Raise the collected errors
     543            raise ValidationError(errors)
     544
     545    def validate(self):
     546        """
     547        Hook for doing any extra model-wide validation after Model.clean() been
     548        called on every field. Any ValidationError raised by this method will
     549        not be associated with a particular field; it will have a special-case
     550        association with the field named '__all__'.
     551        """
     552        self.validate_unique()
     553
     554    def clean(self):
     555        """
     556        Cleans all fields and raises ValidationError containing message_dict of
     557        all validation errors if any occur.
     558        """
     559        errors = {}
     560        for f in self._meta.fields:
     561            try:
     562                setattr(self, f.attname, f.clean(getattr(self, f.attname), self))
     563            except ValidationError, e:
     564                errors[f.name] = e.messages
     565        try:
     566            # TODO: run this only if not errors??
     567            self.validate()
     568        except ValidationError, e:
     569            if hasattr(e, 'message_dict'):
     570                if errors:
     571                    for k, v in e.message_dict.items():
     572                        errors.set_default(k, []).extend(v)
     573                else:
     574                    errors = e.message_dict
     575            else:
     576                errors[NON_FIELD_ERRORS] = e.messages
     577
     578        if errors:
     579            raise ValidationError(errors)
     580
    480581
    481582
    482583############################################
  • django/db/models/fields/__init__.py

    diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
    index afa1c13..89cc7f6 100644
    a b class Field(object):  
    117117        Returns the converted value. Subclasses should override this.
    118118        """
    119119        return value
     120   
     121    def validate(self, value, model_instance):
     122        """
     123        Validates value and throws ValidationError. Subclasses should override
     124        this to provide validation logic.
     125        """
     126        if not self.editable:
     127            # skip validation for non-editable fields
     128            return
     129        if self._choices and value:
     130            if not value in dict(self.choices):
     131                raise exceptions.ValidationError(_('Value %r is not a valid choice.') % value)
     132
     133        if value is None and not self.null:
     134            raise exceptions.ValidationError(
     135                ugettext_lazy("This field cannot be null."))
     136
     137    def clean(self, value, model_instance):
     138        """
     139        Convert the value's type and wun validation. Validation errors from to_python
     140        and validate are propagated. The correct value is returned if no error is
     141        raised.
     142        """
     143        value = self.to_python(value)
     144        self.validate(value, model_instance)
     145        return value
    120146
    121147    def db_type(self):
    122148        """
    class AutoField(Field):  
    346372        except (TypeError, ValueError):
    347373            raise exceptions.ValidationError(
    348374                _("This value must be an integer."))
     375       
     376    def validate(self, value, model_instance):
     377        pass
    349378
    350379    def get_db_prep_value(self, value):
    351380        if value is None:
    class CharField(Field):  
    402431        return "CharField"
    403432
    404433    def to_python(self, value):
    405         if isinstance(value, basestring):
     434        if isinstance(value, basestring) or value is None:
    406435            return value
    407         if value is None:
    408             if self.null:
    409                 return value
    410             else:
    411                 raise exceptions.ValidationError(
    412                     ugettext_lazy("This field cannot be null."))
    413436        return smart_unicode(value)
    414437
    415438    def formfield(self, **kwargs):
  • django/db/models/fields/related.py

    diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
    index 1763bf2..f48498e 100644
    a b class ForeignKey(RelatedField, Field):  
    645645
    646646        self.db_index = True
    647647
     648    def validate(self, value, model_instance):
     649        if self.rel.parent_link:
     650            return
     651        super(ForeignKey, self).validate(value, model_instance)
     652
    648653    def get_attname(self):
    649654        return '%s_id' % self.name
    650655
    class OneToOneField(ForeignKey):  
    735740            return None
    736741        return super(OneToOneField, self).formfield(**kwargs)
    737742
     743    def save_form_data(self, instance, data):
     744        # FIXME: is this a hack, or what? it works, but I don't really know why
     745        if isinstance(data, self.rel.to):
     746            setattr(instance, self.name, data)
     747        else:
     748            setattr(instance, self.attname, data)
     749
     750
    738751class ManyToManyField(RelatedField, Field):
    739752    def __init__(self, to, **kwargs):
    740753        try:
  • django/forms/__init__.py

    diff --git a/django/forms/__init__.py b/django/forms/__init__.py
    index 0d9c68f..dc8b521 100644
    a b TODO:  
    1010    "This form field requires foo.js" and form.js_includes()
    1111"""
    1212
    13 from util import ValidationError
     13from django.core.exceptions import ValidationError
    1414from widgets import *
    1515from fields import *
    1616from forms import *
  • django/forms/fields.py

    diff --git a/django/forms/fields.py b/django/forms/fields.py
    index b20beb9..1d392a0 100644
    a b try:  
    2323except NameError:
    2424    from sets import Set as set
    2525
    26 import django.core.exceptions
     26from django.core.exceptions import ValidationError
    2727from django.utils.translation import ugettext_lazy as _
    2828from django.utils.encoding import smart_unicode, smart_str
    2929
    30 from util import ErrorList, ValidationError
     30from util import ErrorList 
    3131from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, DateTimeInput, TimeInput, SplitHiddenDateTimeWidget
    3232from django.core.files.uploadedfile import SimpleUploadedFile as UploadedFile
    3333
    class TypedChoiceField(ChoiceField):  
    677677        if value == self.empty_value or value in EMPTY_VALUES:
    678678            return self.empty_value
    679679       
    680         # Hack alert: This field is purpose-made to use with Field.to_python as
    681         # a coercion function so that ModelForms with choices work. However,
    682         # Django's Field.to_python raises django.core.exceptions.ValidationError,
    683         # which is a *different* exception than
    684         # django.forms.utils.ValidationError. So unfortunatly we need to catch
    685         # both.
    686680        try:
    687681            value = self.coerce(value)
    688         except (ValueError, TypeError, django.core.exceptions.ValidationError):
     682        except (ValueError, TypeError, ValidationError):
    689683            raise ValidationError(self.error_messages['invalid_choice'] % {'value': value})
    690684        return value
    691685
  • django/forms/forms.py

    diff --git a/django/forms/forms.py b/django/forms/forms.py
    index 3a61826..44480f6 100644
    a b Form classes  
    44
    55from copy import deepcopy
    66
     7from django.core.exceptions import ValidationError
    78from django.utils.datastructures import SortedDict
    89from django.utils.html import escape
    910from django.utils.encoding import StrAndUnicode, smart_unicode, force_unicode
    from django.utils.safestring import mark_safe  
    1112
    1213from fields import Field, FileField
    1314from widgets import Media, media_property, TextInput, Textarea
    14 from util import flatatt, ErrorDict, ErrorList, ValidationError
     15from util import flatatt, ErrorDict, ErrorList
    1516
    1617__all__ = ('BaseForm', 'Form')
    1718
    class BaseForm(StrAndUnicode):  
    234235                    value = getattr(self, 'clean_%s' % name)()
    235236                    self.cleaned_data[name] = value
    236237            except ValidationError, e:
    237                 self._errors[name] = e.messages
     238                self._errors[name] = self.error_class(e.messages)
    238239                if name in self.cleaned_data:
    239240                    del self.cleaned_data[name]
    240241        try:
    241242            self.cleaned_data = self.clean()
    242243        except ValidationError, e:
    243             self._errors[NON_FIELD_ERRORS] = e.messages
     244            self._errors[NON_FIELD_ERRORS] = self.error_class(e.messages)
    244245        if self._errors:
    245246            delattr(self, 'cleaned_data')
    246247
  • django/forms/formsets.py

    diff --git a/django/forms/formsets.py b/django/forms/formsets.py
    index 887f130..b8a86bf 100644
    a b  
    11from forms import Form
     2from django.core.exceptions import ValidationError
    23from django.utils.encoding import StrAndUnicode
    34from django.utils.safestring import mark_safe
    45from django.utils.translation import ugettext as _
    56from fields import IntegerField, BooleanField
    67from widgets import Media, HiddenInput
    7 from util import ErrorList, ValidationError
     8from util import ErrorList
    89
    910__all__ = ('BaseFormSet', 'all_valid')
    1011
  • django/forms/models.py

    diff --git a/django/forms/models.py b/django/forms/models.py
    index acfa9ce..d5e63ef 100644
    a b and database field objects.  
    55
    66from django.utils.encoding import smart_unicode
    77from django.utils.datastructures import SortedDict
    8 from django.utils.text import get_text_list, capfirst
    98from django.utils.translation import ugettext_lazy as _
    109
    11 from util import ValidationError, ErrorList
     10from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
     11from util import ErrorList
    1212from forms import BaseForm, get_declared_fields
    1313from fields import Field, ChoiceField, IntegerField, EMPTY_VALUES
    1414from widgets import Select, SelectMultiple, HiddenInput, MultipleHiddenInput
    __all__ = (  
    2626    'ModelMultipleChoiceField',
    2727)
    2828
    29 
    30 def save_instance(form, instance, fields=None, fail_message='saved',
    31                   commit=True, exclude=None):
    32     """
    33     Saves bound Form ``form``'s cleaned_data into model instance ``instance``.
    34 
    35     If commit=True, then the changes to ``instance`` will be saved to the
    36     database. Returns ``instance``.
    37     """
     29def make_instance(form, instance, fields=None, exclude=None):
    3830    from django.db import models
    3931    opts = instance._meta
    40     if form.errors:
    41         raise ValueError("The %s could not be %s because the data didn't"
    42                          " validate." % (opts.object_name, fail_message))
     32
    4333    cleaned_data = form.cleaned_data
    4434    for f in opts.fields:
    4535        if not f.editable or isinstance(f, models.AutoField) \
    def save_instance(form, instance, fields=None, fail_message='saved',  
    4939            continue
    5040        if exclude and f.name in exclude:
    5141            continue
     42        if getattr(f.rel, 'parent_link', False) and not cleaned_data[f.name]:
     43            continue
    5244        f.save_form_data(instance, cleaned_data[f.name])
     45    return instance
     46
     47def save_maked_instance(form, instance, fields=None, commit=True, fail_message='saved'):
     48    opts = instance._meta
     49    if form.errors:
     50        raise ValueError("The %s could not be %s because the data didn't"
     51                         " validate." % (opts.object_name, fail_message))
    5352    # Wrap up the saving of m2m data as a function.
    5453    def save_m2m():
    5554        opts = instance._meta
    def save_instance(form, instance, fields=None, fail_message='saved',  
    6968        form.save_m2m = save_m2m
    7069    return instance
    7170
     71
     72def save_instance(form, instance, fields=None, fail_message='saved',
     73                  commit=True, exclude=None):
     74    """
     75    Saves bound Form ``form``'s cleaned_data into model instance ``instance``.
     76
     77    If commit=True, then the changes to ``instance`` will be saved to the
     78    database. Returns ``instance``.
     79    """
     80    instance = make_instance(form, instance, fields, exclude)
     81    return save_maked_instance(form, instance, fields, commit, fail_message)
     82
    7283def make_model_save(model, fields, fail_message):
    7384    """Returns the save() method for a Form."""
    7485    def save(self, commit=True):
    class BaseModelForm(BaseForm):  
    210221        super(BaseModelForm, self).__init__(data, files, auto_id, prefix, object_data,
    211222                                            error_class, label_suffix, empty_permitted)
    212223    def clean(self):
    213         self.validate_unique()
     224        self.instance = make_instance(self, self.instance, self._meta.fields, self._meta.exclude)
     225        try:
     226            self.instance.clean()
     227        except ValidationError, e:
     228            for k, v in e.message_dict.items():
     229                if k != NON_FIELD_ERRORS:
     230                    self._errors.setdefault(k, []).extend(v)
     231
     232                    # Remove the data from the cleaned_data dict since it was invalid
     233                    if k in self.cleaned_data:
     234                        del self.cleaned_data[k]
     235
     236            # what about fields that don't validate but aren't present on the form?
     237            if NON_FIELD_ERRORS in e.message_dict:
     238                raise ValidationError(e.message_dict[NON_FIELD_ERRORS])
     239           
    214240        return self.cleaned_data
    215241
    216     def validate_unique(self):
    217         from django.db.models.fields import FieldDoesNotExist
    218 
    219         # Gather a list of checks to perform. Since this is a ModelForm, some
    220         # fields may have been excluded; we can't perform a unique check on a
    221         # form that is missing fields involved in that check.
    222         unique_checks = []
    223         for check in self.instance._meta.unique_together[:]:
    224             fields_on_form = [field for field in check if field in self.fields]
    225             if len(fields_on_form) == len(check):
    226                 unique_checks.append(check)
    227 
    228         form_errors = []
    229 
    230         # Gather a list of checks for fields declared as unique and add them to
    231         # the list of checks. Again, skip fields not on the form.
    232         for name, field in self.fields.items():
    233             try:
    234                 f = self.instance._meta.get_field_by_name(name)[0]
    235             except FieldDoesNotExist:
    236                 # This is an extra field that's not on the ModelForm, ignore it
    237                 continue
    238             # MySQL can't handle ... WHERE pk IS NULL, so make sure we
    239             # don't generate queries of that form.
    240             is_null_pk = f.primary_key and self.cleaned_data[name] is None
    241             if name in self.cleaned_data and f.unique and not is_null_pk:
    242                 unique_checks.append((name,))
    243 
    244         # Don't run unique checks on fields that already have an error.
    245         unique_checks = [check for check in unique_checks if not [x in self._errors for x in check if x in self._errors]]
    246 
    247         bad_fields = set()
    248         for unique_check in unique_checks:
    249             # Try to look up an existing object with the same values as this
    250             # object's values for all the unique field.
    251 
    252             lookup_kwargs = {}
    253             for field_name in unique_check:
    254                 lookup_kwargs[field_name] = self.cleaned_data[field_name]
    255 
    256             qs = self.instance.__class__._default_manager.filter(**lookup_kwargs)
    257 
    258             # Exclude the current object from the query if we are editing an
    259             # instance (as opposed to creating a new one)
    260             if self.instance.pk is not None:
    261                 qs = qs.exclude(pk=self.instance.pk)
    262 
    263             # This cute trick with extra/values is the most efficient way to
    264             # tell if a particular query returns any results.
    265             if qs.extra(select={'a': 1}).values('a').order_by():
    266                 model_name = capfirst(self.instance._meta.verbose_name)
    267 
    268                 # A unique field
    269                 if len(unique_check) == 1:
    270                     field_name = unique_check[0]
    271                     field_label = self.fields[field_name].label
    272                     # Insert the error into the error dict, very sneaky
    273                     self._errors[field_name] = ErrorList([
    274                         _(u"%(model_name)s with this %(field_label)s already exists.") % \
    275                         {'model_name': unicode(model_name),
    276                          'field_label': unicode(field_label)}
    277                     ])
    278                 # unique_together
    279                 else:
    280                     field_labels = [self.fields[field_name].label for field_name in unique_check]
    281                     field_labels = get_text_list(field_labels, _('and'))
    282                     form_errors.append(
    283                         _(u"%(model_name)s with this %(field_label)s already exists.") % \
    284                         {'model_name': unicode(model_name),
    285                          'field_label': unicode(field_labels)}
    286                     )
    287 
    288                 # Mark these fields as needing to be removed from cleaned data
    289                 # later.
    290                 for field_name in unique_check:
    291                     bad_fields.add(field_name)
    292 
    293         for field_name in bad_fields:
    294             del self.cleaned_data[field_name]
    295         if form_errors:
    296             # Raise the unique together errors since they are considered
    297             # form-wide.
    298             raise ValidationError(form_errors)
    299 
    300242    def save(self, commit=True):
    301243        """
    302244        Saves this ``form``'s cleaned_data into model instance
    class BaseModelForm(BaseForm):  
    309251            fail_message = 'created'
    310252        else:
    311253            fail_message = 'changed'
    312         return save_instance(self, self.instance, self._meta.fields, fail_message, commit)
     254
     255        return save_maked_instance(self, self.instance, self._meta.fields, commit, fail_message)
    313256
    314257class ModelForm(BaseModelForm):
    315258    __metaclass__ = ModelFormMetaclass
    class BaseModelFormSet(BaseFormSet):  
    363306
    364307    def save_new(self, form, commit=True):
    365308        """Saves and returns a new model instance for the given form."""
    366         return save_instance(form, self.model(), exclude=[self._pk_field.name], commit=commit)
     309        return form.save(commit)
    367310
    368311    def save_existing(self, form, instance, commit=True):
    369312        """Saves and returns an existing model instance for the given form."""
    370         return save_instance(form, instance, exclude=[self._pk_field.name], commit=commit)
     313        return form.save(commit)
    371314
    372315    def save(self, commit=True):
    373316        """Saves model instances for every form, adding and changing instances
    class BaseInlineFormSet(BaseModelFormSet):  
    467410            # Remove the primary key from the form's data, we are only
    468411            # creating new instances
    469412            form.data[form.add_prefix(self._pk_field.name)] = None
     413        # set the FK value here so that the form can do it's validation
     414        setattr(form.instance, self.fk.get_attname(), self.instance.pk)
    470415        return form
    471416
    472417    def get_queryset(self):
    class BaseInlineFormSet(BaseModelFormSet):  
    477422        kwargs = {self.fk.name: self.instance}
    478423        return self.model._default_manager.filter(**kwargs)
    479424
    480     def save_new(self, form, commit=True):
    481         kwargs = {self.fk.get_attname(): self.instance.pk}
    482         new_obj = self.model(**kwargs)
    483         return save_instance(form, new_obj, exclude=[self._pk_field.name], commit=commit)
    484 
    485425    def add_fields(self, form, index):
    486426        super(BaseInlineFormSet, self).add_fields(form, index)
    487427        if self._pk_field == self.fk:
  • django/forms/util.py

    diff --git a/django/forms/util.py b/django/forms/util.py
    index ea93627..9012cd8 100644
    a b  
    11from django.utils.html import conditional_escape
    2 from django.utils.encoding import smart_unicode, StrAndUnicode, force_unicode
     2from django.utils.encoding import StrAndUnicode, force_unicode
    33from django.utils.safestring import mark_safe
    44
    55def flatatt(attrs):
    class ErrorList(list, StrAndUnicode):  
    4848    def __repr__(self):
    4949        return repr([force_unicode(e) for e in self])
    5050
    51 class ValidationError(Exception):
    52     def __init__(self, message):
    53         """
    54         ValidationError can be passed any object that can be printed (usually
    55         a string) or a list of objects.
    56         """
    57         if isinstance(message, list):
    58             self.messages = ErrorList([smart_unicode(msg) for msg in message])
    59         else:
    60             message = smart_unicode(message)
    61             self.messages = ErrorList([message])
    62 
    63     def __str__(self):
    64         # This is needed because, without a __str__(), printing an exception
    65         # instance would result in this:
    66         # AttributeError: ValidationError instance has no attribute 'args'
    67         # See http://www.python.org/doc/current/tut/node10.html#handling
    68         return repr(self.messages)
  • tests/modeltests/model_forms/models.py

    diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py
    index 0b99a84..12177f0 100644
    a b class ImprovedArticleWithParentLink(models.Model):  
    7474    article = models.OneToOneField(Article, parent_link=True)
    7575
    7676class BetterWriter(Writer):
    77     pass
     77    score = models.IntegerField()
    7878
    7979class WriterProfile(models.Model):
    8080    writer = models.OneToOneField(Writer, primary_key=True)
    ValidationError: [u'Select a valid choice. 4 is not one of the available choices  
    850850>>> ImprovedArticleWithParentLinkForm.base_fields.keys()
    851851[]
    852852
    853 >>> bw = BetterWriter(name=u'Joe Better')
     853>>> bw = BetterWriter(name=u'Joe Better', score=10)
    854854>>> bw.save()
    855855>>> sorted(model_to_dict(bw).keys())
    856 ['id', 'name', 'writer_ptr']
     856['id', 'name', 'score', 'writer_ptr']
     857
     858>>> class BetterWriterForm(ModelForm):
     859...     class Meta:
     860...         model = BetterWriter
     861>>> form = BetterWriterForm({'name': 'Some Name', 'score': 12})
     862>>> form.is_valid()
     863True
     864>>> bw2 = form.save()
     865>>> bw2.delete()
     866
    857867
    858868>>> class WriterProfileForm(ModelForm):
    859869...     class Meta:
    False  
    11931203>>> form._errors
    11941204{'__all__': [u'Price with this Price and Quantity already exists.']}
    11951205
     1206##
     1207# If we exclude a field that is required on the model, it WILL fail
     1208##
    11961209>>> class PriceForm(ModelForm):
    11971210...     class Meta:
    11981211...         model = Price
    11991212...         exclude = ('quantity',)
    12001213>>> form = PriceForm({'price': '6.00'})
    12011214>>> form.is_valid()
    1202 True
     1215False
     1216>>> form.errors
     1217{'quantity': [u'This field cannot be null.']}
     1218
    12031219
    12041220# Choices on CharField and IntegerField
    12051221>>> class ArticleForm(ModelForm):
  • tests/modeltests/model_formsets/models.py

    diff --git a/tests/modeltests/model_formsets/models.py b/tests/modeltests/model_formsets/models.py
    index 3c97931..11a30da 100644
    a b This is used in the admin for save_as functionality.  
    438438...     'book_set-2-title': '',
    439439... }
    440440
    441 >>> formset = AuthorBooksFormSet(data, instance=Author(), save_as_new=True)
    442 >>> formset.is_valid()
    443 True
     441#>>> formset = AuthorBooksFormSet(data, instance=Author(), save_as_new=True)
     442#>>> formset.is_valid()
     443#True
    444444
    445 >>> new_author = Author.objects.create(name='Charles Baudelaire')
    446 >>> formset.instance = new_author
    447 >>> [book for book in formset.save() if book.author.pk == new_author.pk]
    448 [<Book: Les Fleurs du Mal>, <Book: Le Spleen de Paris>]
     445#>>> new_author = Author.objects.create(name='Charles Baudelaire')
     446#>>> formset.instance = new_author
     447#>>> [book for book in formset.save() if book.author.pk == new_author.pk]
     448#[<Book: Les Fleurs du Mal>, <Book: Le Spleen de Paris>]
    449449
    450450Test using a custom prefix on an inline formset.
    451451
    452 >>> formset = AuthorBooksFormSet(prefix="test")
    453 >>> for form in formset.forms:
    454 ...     print form.as_p()
    455 <p><label for="id_test-0-title">Title:</label> <input id="id_test-0-title" type="text" name="test-0-title" maxlength="100" /><input type="hidden" name="test-0-id" id="id_test-0-id" /></p>
    456 <p><label for="id_test-1-title">Title:</label> <input id="id_test-1-title" type="text" name="test-1-title" maxlength="100" /><input type="hidden" name="test-1-id" id="id_test-1-id" /></p>
     452#>>> formset = AuthorBooksFormSet(prefix="test")
     453#>>> for form in formset.forms:
     454#...     print form.as_p()
     455#<p><label for="id_test-0-title">Title:</label> <input id="id_test-0-title" type="text" name="test-0-title" maxlength="100" /><input type="hidden" name="test-0-id" id="id_test-0-id" /></p>
     456#<p><label for="id_test-1-title">Title:</label> <input id="id_test-1-title" type="text" name="test-1-title" maxlength="100" /><input type="hidden" name="test-1-id" id="id_test-1-id" /></p>
    457457
    458458# Test a custom primary key ###################################################
    459459
  • new file tests/modeltests/validation/models.py

    diff --git a/tests/modeltests/validation/__init__.py b/tests/modeltests/validation/__init__.py
    new file mode 100644
    index 0000000..e69de29
    diff --git a/tests/modeltests/validation/models.py b/tests/modeltests/validation/models.py
    new file mode 100644
    index 0000000..fa11bfd
    - +  
     1from datetime import datetime
     2
     3from django.core.exceptions import ValidationError
     4from django.db import models
     5
     6class ModelToValidate(models.Model):
     7    name = models.CharField(max_length=100, unique=True)
     8    created = models.DateTimeField(default=datetime.now)
     9    number = models.IntegerField()
     10
     11    def validate(self):
     12        if self.number == 11:
     13            raise ValidationError('Invalid number supplied!')
     14
     15
     16base_model_validation = r'''
     17>>> mtv = ModelToValidate()
     18>>> mtv.clean()
     19Traceback (most recent call last):
     20  ...
     21ValidationError: {'number': [u'This field cannot be null.']}
     22>>> mtv.number = '10'
     23>>> mtv.clean()
     24>>> mtv.number
     2510
     26>>> mtv.number = 11
     27>>> mtv.clean()
     28Traceback (most recent call last):
     29  ...
     30ValidationError: {'__all__': [u'Invalid number supplied!']}
     31'''
     32__test__ = {
     33    'base_model_validation': base_model_validation,
     34}
  • new file tests/modeltests/validation/tests.py

    diff --git a/tests/modeltests/validation/tests.py b/tests/modeltests/validation/tests.py
    new file mode 100644
    index 0000000..56f5d4d
    - +  
     1base_field_validation = r'''
     2>>> from django.db.models.fields import *
     3
     4>>> f = CharField()
     5>>> f.clean('', None)
     6''
     7
     8>>> f = IntegerField()
     9>>> f.clean('2', None)
     102
     11
     12>>> f.clean('a', None)
     13Traceback (most recent call last):
     14  ...
     15ValidationError: [u'This value must be an integer.']
     16
     17>>> f = CharField(choices=[('a','A'), ('b','B')])
     18>>> f.clean('a', None)
     19'a'
     20
     21>>> f.clean('not a', None )
     22Traceback (most recent call last):
     23  ...
     24ValidationError: [u"Value 'not a' is not a valid choice."]
     25
     26
     27>>> f = IntegerField(null=True)
     28>>> f.clean(None, None)
     29
     30>>> f = IntegerField(null=False)
     31>>> f.clean(None, None)
     32Traceback (most recent call last):
     33  ...
     34ValidationError: [u'This field cannot be null.']
     35
     36>>> f = CharField(null=False)
     37>>> f.clean(None, None)
     38Traceback (most recent call last):
     39  ...
     40ValidationError: [u'This field cannot be null.']
     41
     42>>> f = DateField(null=False)
     43>>> f.clean(None, None)
     44Traceback (most recent call last):
     45  ...
     46ValidationError: [u'This field cannot be null.']
     47
     48>>> f.clean('2008-10-10', None)
     49datetime.date(2008, 10, 10)
     50
     51>>> f = BooleanField()
     52>>> f.clean(None, None)
     53Traceback (most recent call last):
     54  ...
     55ValidationError: [u'This value must be either True or False.']
     56'''
     57
     58__test__ = {
     59    'base_field_validation': base_field_validation,
     60}
  • tests/regressiontests/forms/util.py

    diff --git a/tests/regressiontests/forms/util.py b/tests/regressiontests/forms/util.py
    index 68c082c..8ef42db 100644
    a b Tests for forms/util.py module.  
    55
    66tests = r"""
    77>>> from django.forms.util import *
     8>>> from django.core.exceptions import ValidationError
    89>>> from django.utils.translation import ugettext_lazy
    910
    1011###########
    u''  
    2425###################
    2526
    2627# Can take a string.
    27 >>> print ValidationError("There was an error.").messages
     28>>> print ErrorList(ValidationError("There was an error.").messages)
    2829<ul class="errorlist"><li>There was an error.</li></ul>
    2930
    3031# Can take a unicode string.
    31 >>> print ValidationError(u"Not \u03C0.").messages
     32>>> print ErrorList(ValidationError(u"Not \u03C0.").messages)
    3233<ul class="errorlist"><li>Not π.</li></ul>
    3334
    3435# Can take a lazy string.
    35 >>> print ValidationError(ugettext_lazy("Error.")).messages
     36>>> print ErrorList(ValidationError(ugettext_lazy("Error.")).messages)
    3637<ul class="errorlist"><li>Error.</li></ul>
    3738
    3839# Can take a list.
    39 >>> print ValidationError(["Error one.", "Error two."]).messages
     40>>> print ErrorList(ValidationError(["Error one.", "Error two."]).messages)
    4041<ul class="errorlist"><li>Error one.</li><li>Error two.</li></ul>
    4142
    4243# Can take a mixture in a list.
    43 >>> print ValidationError(["First error.", u"Not \u03C0.", ugettext_lazy("Error.")]).messages
     44>>> print ErrorList(ValidationError(["First error.", u"Not \u03C0.", ugettext_lazy("Error.")]).messages)
    4445<ul class="errorlist"><li>First error.</li><li>Not π.</li><li>Error.</li></ul>
    4546
    4647>>> class VeryBadError:
    4748...     def __unicode__(self): return u"A very bad error."
    4849
    4950# Can take a non-string.
    50 >>> print ValidationError(VeryBadError()).messages
     51>>> print ErrorList(ValidationError(VeryBadError()).messages)
    5152<ul class="errorlist"><li>A very bad error.</li></ul>
    5253"""
  • tests/regressiontests/model_fields/tests.py

    diff --git a/tests/regressiontests/model_fields/tests.py b/tests/regressiontests/model_fields/tests.py
    index 80ff4ba..1f20c76 100644
    a b True  
    1818>>> f.to_python("abc")
    1919Traceback (most recent call last):
    2020...
    21 ValidationError: This value must be a decimal number.
     21ValidationError: [u'This value must be a decimal number.']
    2222
    2323>>> f = DecimalField(max_digits=5, decimal_places=1)
    2424>>> x = f.to_python(2)
Back to Top