Opened 5 months ago
Closed 5 months ago
#35550 closed Bug (duplicate)
UniqueConstraint with condition seems not checked in BaseInlineFormSet
Reported by: | Serl | Owned by: | nobody |
---|---|---|---|
Component: | Forms | Version: | 5.0 |
Severity: | Normal | Keywords: | |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
Hello,
I spotted a corner case for which we have an IntegrityError
instead of a ValidationError
.
I've put together a demo project with all the details, the most important follows.
I have a model like so:
class Product(models.Model): design = models.ForeignKey(...) type = models.CharField(...) size = models.CharField(null=True, ...) ... class Meta: constraints = [ models.UniqueConstraint( fields=["design", "type"], condition=models.Q(size__isnull=True), name="unique_design_type", ), ... ]
I'm using the standard forms in the admin site.
If I try to violate that UniqueConstraint
from ProductAdmin
, I correctly get a validation error. I materialized this check in a test case TestFormValidation.test_validation_unique_design_type
.
If I try to violate it from the formset in ProductInlineAdmin
in DesignAdmin
, I get a 500 error. The test case TestFormSetValidation.test_validation_unique_design_type
shows the expected and actual behavior:
def test_validation_unique_design_type(self): ... formset = FormSet( data={ f"{prefix}-INITIAL_FORMS": "0", f"{prefix}-TOTAL_FORMS": "2", f"{prefix}-MAX_NUM_FORMS": "1000", f"{prefix}-0-type": "Mug", f"{prefix}-0-price": "2", f"{prefix}-1-type": "Mug", f"{prefix}-1-price": "2", }, instance=self.design, ) # EXPECTED BEHAVIOR self.assertFalse(formset.is_valid()) with self.assertRaisesMessage(ValueError, "didn't validate"): formset.save() # ACTUAL BEHAVIOR self.assertTrue(formset.is_valid()) with self.assertRaisesMessage(IntegrityError, "UNIQUE constraint failed"): formset.save()
Note that in the demo project I also added a second UniqueConstraint
with three fields and no condition, and that works like a charm (test cases there to prove). That second constraint actually show the pattern that I use in the main project, which is to have an unique constraint on three fields, one of which is nullable - that is nulls_distinct=False
before updating to Django 5 and Postgres 15. But I digress.
As a workaround for may day job I implemented a custom clean
in my inline formsets.
If you agree on the buggy nature of this behavior, I'd like to participate with a PR. Let me know your thoughts.
Thanks for the ticket.
UniqueConstraint
withexpressions
and partial unique constraints are ignored on purpose, see #33335.Duplicate of #23964.