#25987: Creating new object on adminsite with child model inlines and
unique_together raises IntegrityError
-------------------------------------+-------------------------------------
     Reporter:  MrYoda               |                    Owner:  nobody
         Type:  Bug                  |                   Status:  new
    Component:  Forms                |                  Version:
     Severity:  Normal               |               Resolution:
     Keywords:  admin, create        |             Triage Stage:
  object, unique_together,           |  Unreviewed
  IntegrityError, validate_unique    |
    Has patch:  0                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------

Comment (by MrYoda):

 I went deeper in formsets and wrote this for ChildModel:

 {{{#!python
 class ChildModelFormSet(BaseInlineFormSet):
     def clean(self):
         super().clean()
         self.validate_unique_on_create()

     def validate_unique_on_create(self):
         """ Additional validation of unique
         Copy-paste from BaseModelFormSet.validate_unique(), but removed
 date checks and added "or 0" condition in row_data list comprehension to
 prevent "None" values for not yet saved objects.
         """
         # Collect unique_checks and date_checks to run from all the forms.
         all_unique_checks = set()
         forms_to_delete = self.deleted_forms
         valid_forms = [form for form in self.forms if form.is_valid() and
 form not in forms_to_delete]
         for form in valid_forms:
             exclude = form._get_validation_exclusions()
             unique_checks, _ =
 form.instance._get_unique_checks(exclude=exclude)
             all_unique_checks =
 all_unique_checks.union(set(unique_checks))

         errors = []
         # Do each of the unique checks (unique and unique_together)
         for uclass, unique_check in all_unique_checks:
             seen_data = set()
             for form in valid_forms:
                 # get data for each field of each of unique_check
                 row_data = (form.cleaned_data[field]
                             for field in unique_check if field in
 form.cleaned_data)
                 # Reduce Model instances to their primary key values
                 row_data = tuple(d._get_pk_val() or 0 if hasattr(d,
 '_get_pk_val') else d
                                  for d in row_data)
                 if row_data and None not in row_data:
                     # if we've already seen it then we have a uniqueness
 failure
                     if row_data in seen_data:
                         # poke error messages into the right places and
 mark
                         # the form as invalid
 errors.append(self.get_unique_error_message(unique_check))
                         form._errors[NON_FIELD_ERRORS] =
 self.error_class([self.get_form_error()])
                         # remove the data from the cleaned_data dict since
 it was invalid
                         for field in unique_check:
                             if field in form.cleaned_data:
                                 del form.cleaned_data[field]
                     # mark the data as seen
                     seen_data.add(row_data)
         if errors:
             raise ValidationError(errors)

 }}}

 It works fine and gracefully as I think. Workaround or not?

--
Ticket URL: <https://code.djangoproject.com/ticket/25987#comment:2>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-updates+unsubscr...@googlegroups.com.
To post to this group, send email to django-updates@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/064.b16eeeebb6bdf498b646dd111880883d%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to