Ticket #12521: only_validate_fields_on_modelform_not_entire_model.diff

File only_validate_fields_on_modelform_not_entire_model.diff, 6.6 KB (added by Honza Král, 14 years ago)

Simple quick patch implementing desired behavior, just a case study, still needs work

  • django/core/exceptions.py

    diff --git a/django/core/exceptions.py b/django/core/exceptions.py
    index fee7db4..a91e61a 100644
    a b class FieldError(Exception):  
    3333    pass
    3434
    3535NON_FIELD_ERRORS = '__all__'
    36 class BaseValidationError(Exception):
     36class ValidationError(Exception):
    3737    """An error while validating data."""
    3838    def __init__(self, message, code=None, params=None):
    3939        import operator
    class BaseValidationError(Exception):  
    6464            return repr(self.message_dict)
    6565        return repr(self.messages)
    6666
    67 class ValidationError(BaseValidationError):
    68     pass
    69 
    70 class UnresolvableValidationError(BaseValidationError):
    71     """Validation error that cannot be resolved by the user."""
    72     pass
    73 
  • django/db/models/base.py

    diff --git a/django/db/models/base.py b/django/db/models/base.py
    index 06db7cc..64b4993 100644
    a b class Model(object):  
    649649        not be associated with a particular field; it will have a special-case
    650650        association with the field defined by NON_FIELD_ERRORS.
    651651        """
    652         self.validate_unique()
     652        pass
    653653
    654     def validate_unique(self):
    655         unique_checks, date_checks = self._get_unique_checks()
     654    def validate_unique(self, exclude=[]):
     655        unique_checks, date_checks = self._get_unique_checks(exclude)
    656656
    657657        errors = self._perform_unique_checks(unique_checks)
    658658        date_errors = self._perform_date_checks(date_checks)
    class Model(object):  
    663663        if errors:
    664664            raise ValidationError(errors)
    665665
    666     def _get_unique_checks(self):
     666    def _get_unique_checks(self, exclude=[]):
    667667        from django.db.models.fields import FieldDoesNotExist, Field as ModelField
    668668
    669         unique_checks = list(self._meta.unique_together)
     669        unique_checks = []
     670        # include all unique_together checks...
     671        for check in self._meta.unique_together:
     672            for name in check:
     673                # ... except those that contain an excluded field
     674                if name in exclude:
     675                    break
     676            else:
     677                unique_checks.append(check)
     678
    670679        # these are checks for the unique_for_<date/year/month>
    671680        date_checks = []
    672681
    class Model(object):  
    674683        # the list of checks. Again, skip empty fields and any that did not validate.
    675684        for f in self._meta.fields:
    676685            name = f.name
     686            # do not process excluded fields
     687            if name in exclude:
     688                continue
    677689            if f.unique:
    678690                unique_checks.append((name,))
    679691            if f.unique_for_date:
    class Model(object):  
    795807            except ValidationError, e:
    796808                errors[f.name] = e.messages
    797809
    798         # Form.clean() is run even if other validation fails, so do the
    799         # same with Model.validate() for consistency.
    800810        try:
    801             self.validate()
     811            self.validate_unique(exclude=exclude+errors.keys())
    802812        except ValidationError, e:
    803813            if hasattr(e, 'message_dict'):
    804814                if errors:
    class Model(object):  
    809819            else:
    810820                errors[NON_FIELD_ERRORS] = e.messages
    811821
     822        # Form.clean() is run even if other validation fails, so do the
     823        # same with Model.validate() for consistency.
     824
     825
     826        # However, do not run Model.validate() on incomplete forms when creating new instance
     827        if not (exclude and getattr(self, '_adding', True)):
     828            try:
     829                self.validate()
     830            except ValidationError, e:
     831                if hasattr(e, 'message_dict'):
     832                    if errors:
     833                        for k, v in e.message_dict.items():
     834                            errors.set_default(k, []).extend(v)
     835                    else:
     836                        errors = e.message_dict
     837                else:
     838                    errors[NON_FIELD_ERRORS] = e.messages
     839
    812840        if errors:
    813841            raise ValidationError(errors)
    814842
  • django/forms/models.py

    diff --git a/django/forms/models.py b/django/forms/models.py
    index ff20c93..6b2bca2 100644
    a b from django.utils.datastructures import SortedDict  
    99from django.utils.text import get_text_list, capfirst
    1010from django.utils.translation import ugettext_lazy as _, ugettext
    1111
    12 from django.core.exceptions import ValidationError, NON_FIELD_ERRORS, UnresolvableValidationError
     12from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
    1313from django.core.validators import EMPTY_VALUES
    1414from util import ErrorList
    1515from forms import BaseForm, get_declared_fields
    class BaseModelForm(BaseForm):  
    249249        opts = self._meta
    250250        self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude)
    251251        try:
    252             self.instance.full_validate(exclude=self._errors.keys())
     252            self.instance.full_validate(exclude=self._errors.keys() + list(opts.exclude or []))
    253253        except ValidationError, e:
    254254            for k, v in e.message_dict.items():
    255255                if k != NON_FIELD_ERRORS:
    class BaseModelForm(BaseForm):  
    262262            if NON_FIELD_ERRORS in e.message_dict:
    263263                raise ValidationError(e.message_dict[NON_FIELD_ERRORS])
    264264
    265             # If model validation threw errors for fields that aren't on the
    266             # form, the the errors cannot be corrected by the user. Displaying
    267             # those errors would be pointless, so raise another type of
    268             # exception that *won't* be caught and displayed by the form.
    269             if set(e.message_dict.keys()) - set(self.fields.keys() + [NON_FIELD_ERRORS]):
    270                 raise UnresolvableValidationError(e.message_dict)
    271 
    272 
    273265        return self.cleaned_data
    274266
    275267    def save(self, commit=True):
  • tests/modeltests/model_forms/models.py

    diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py
    index ba59f9a..4ed8da1 100644
    a b False  
    14251425>>> form._errors
    14261426{'__all__': [u'Price with this Price and Quantity already exists.']}
    14271427
    1428 # This form is never valid because quantity is blank=False.
    14291428>>> class PriceForm(ModelForm):
    14301429...     class Meta:
    14311430...         model = Price
    14321431...         exclude = ('quantity',)
    14331432>>> form = PriceForm({'price': '6.00'})
    14341433>>> form.is_valid()
    1435 Traceback (most recent call last):
    1436   ...
    1437 UnresolvableValidationError: {'quantity': [u'This field cannot be null.']}
     1434True
    14381435
    14391436# Unique & unique together with null values
    14401437>>> class BookForm(ModelForm):
Back to Top