#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.