Code

Ticket #18912: 0001-Made-ModelForm-correctly-handle-unique-checks-for-pa.2.patch

File 0001-Made-ModelForm-correctly-handle-unique-checks-for-pa.2.patch, 6.5 KB (added by sebastian_noack, 18 months ago)
  • django/forms/models.py

    From 7f79fae7c686cbaf4b0f6d22feb29445af206677 Mon Sep 17 00:00:00 2001
    From: Sebastian Noack <s.noack@placement24.com>
    Date: Thu, 6 Sep 2012 10:47:52 +0200
    Subject: [PATCH] Made ModelForm correctly handle unique checks for parent
     models.
    
    See http://code.djangoproject.com/ticket/18912
    ---
     django/forms/models.py |   39 ++++++++++++++++++++++-----------------
     1 file changed, 22 insertions(+), 17 deletions(-)
    
    diff --git a/django/forms/models.py b/django/forms/models.py
    index cc43612..392e062 100644
    a b class BaseModelForm(BaseForm): 
    227227                                            error_class, label_suffix, empty_permitted) 
    228228 
    229229    def clean(self): 
    230         self.validate_unique() 
     230        self.validate_unique(self.instance.__class__) 
    231231        return self.cleaned_data 
    232232 
    233     def validate_unique(self): 
    234         unique_checks, date_checks = self._get_unique_checks() 
     233    def validate_unique(self, model): 
     234        unique_checks, date_checks = self._get_unique_checks(model) 
    235235        form_errors = [] 
    236236        bad_fields = set() 
    237237 
    238         field_errors, global_errors = self._perform_unique_checks(unique_checks) 
     238        field_errors, global_errors = self._perform_unique_checks(model, unique_checks) 
    239239        bad_fields.union(field_errors) 
    240240        form_errors.extend(global_errors) 
    241241 
    242         field_errors, global_errors = self._perform_date_checks(date_checks) 
     242        field_errors, global_errors = self._perform_date_checks(model, date_checks) 
    243243        bad_fields.union(field_errors) 
    244244        form_errors.extend(global_errors) 
    245245 
    class BaseModelForm(BaseForm): 
    250250            # form-wide. 
    251251            raise ValidationError(form_errors) 
    252252 
    253     def _get_unique_checks(self): 
     253        for parent_model in model._meta.parents: 
     254            self.validate_unique(parent_model) 
     255 
     256    def _get_unique_checks(self, model): 
    254257        from django.db.models.fields import FieldDoesNotExist, Field as ModelField 
    255258 
    256259        # Gather a list of checks to perform. We only perform unique checks 
    class BaseModelForm(BaseForm): 
    262265        unique_checks = [] 
    263266        # these are checks for the unique_for_<date/year/month> 
    264267        date_checks = [] 
    265         for check in self.instance._meta.unique_together[:]: 
     268        for check in model._meta.unique_together[:]: 
    266269            fields_on_form = [field for field in check if self.cleaned_data.get(field) is not None] 
    267270            if len(fields_on_form) == len(check): 
    268271                unique_checks.append(check) 
    class BaseModelForm(BaseForm): 
    271274        # the list of checks. Again, skip empty fields and any that did not validate. 
    272275        for name in self.fields: 
    273276            try: 
    274                 f = self.instance._meta.get_field_by_name(name)[0] 
     277                f, parent_model, _, _ = model._meta.get_field_by_name(name) 
    275278            except FieldDoesNotExist: 
    276279                # This is an extra field that's not on the ModelForm, ignore it 
    277280                continue 
    class BaseModelForm(BaseForm): 
    281284                # get_field_by_name found it, but it is not a Field so do not proceed 
    282285                # to use it as if it were. 
    283286                continue 
     287            if parent_model: 
     288                continue 
    284289            if self.cleaned_data.get(name) is None: 
    285290                continue 
    286291            if f.unique: 
    class BaseModelForm(BaseForm): 
    294299        return unique_checks, date_checks 
    295300 
    296301 
    297     def _perform_unique_checks(self, unique_checks): 
     302    def _perform_unique_checks(self, model, unique_checks): 
    298303        bad_fields = set() 
    299304        form_errors = [] 
    300305 
    class BaseModelForm(BaseForm): 
    312317                    lookup_value =  lookup_value.pk 
    313318                lookup_kwargs[str(field_name)] = lookup_value 
    314319 
    315             qs = self.instance.__class__._default_manager.filter(**lookup_kwargs) 
     320            qs = model._default_manager.filter(**lookup_kwargs) 
    316321 
    317322            # Exclude the current object from the query if we are editing an 
    318323            # instance (as opposed to creating a new one) 
    class BaseModelForm(BaseForm): 
    323328            # tell if a particular query returns any results. 
    324329            if qs.extra(select={'a': 1}).values('a').order_by(): 
    325330                if len(unique_check) == 1: 
    326                     self._errors[unique_check[0]] = ErrorList([self.unique_error_message(unique_check)]) 
     331                    self._errors[unique_check[0]] = ErrorList([self.unique_error_message(model, unique_check)]) 
    327332                else: 
    328                     form_errors.append(self.unique_error_message(unique_check)) 
     333                    form_errors.append(self.unique_error_message(model, unique_check)) 
    329334 
    330335                # Mark these fields as needing to be removed from cleaned data 
    331336                # later. 
    class BaseModelForm(BaseForm): 
    333338                    bad_fields.add(field_name) 
    334339        return bad_fields, form_errors 
    335340 
    336     def _perform_date_checks(self, date_checks): 
     341    def _perform_date_checks(self, model, date_checks): 
    337342        bad_fields = set() 
    338343        for lookup_type, field, unique_for in date_checks: 
    339344            lookup_kwargs = {} 
    class BaseModelForm(BaseForm): 
    348353                lookup_kwargs['%s__%s' % (unique_for, lookup_type)] = getattr(self.cleaned_data[unique_for], lookup_type) 
    349354            lookup_kwargs[field] = self.cleaned_data[field] 
    350355 
    351             qs = self.instance.__class__._default_manager.filter(**lookup_kwargs) 
     356            qs = model._default_manager.filter(**lookup_kwargs) 
    352357            # Exclude the current object from the query if we are editing an 
    353358            # instance (as opposed to creating a new one) 
    354359            if self.instance.pk is not None: 
    class BaseModelForm(BaseForm): 
    370375            'lookup': lookup_type, 
    371376        } 
    372377 
    373     def unique_error_message(self, unique_check): 
    374         model_name = capfirst(self.instance._meta.verbose_name) 
     378    def unique_error_message(self, model, unique_check): 
     379        model_name = capfirst(model._meta.verbose_name) 
    375380 
    376381        # A unique field 
    377382        if len(unique_check) == 1: 
    class BaseModelFormSet(BaseFormSet): 
    532537                break 
    533538        else: 
    534539            return 
    535         unique_checks, date_checks = form._get_unique_checks() 
     540        unique_checks, date_checks = form._get_unique_checks(self.instance.__class__) 
    536541        errors = [] 
    537542        # Do each of the unique checks (unique and unique_together) 
    538543        for unique_check in unique_checks: