#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 [email protected].
To post to this group, send email to [email protected].
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