#35550: UniqueConstraint with condition seems not checked in BaseInlineFormSet
---------------------------------------+------------------------
Reporter: Sergio Livi | Owner: nobody
Type: Bug | Status: new
Component: Forms | Version: 5.0
Severity: Normal | Keywords:
Triage Stage: Unreviewed | Has patch: 0
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
---------------------------------------+------------------------
Hello,
I spotted a corner case for which we have an `IntegrityError` instead of a
`ValidationError`.
I've put together a [https://github.com/serl/django-fieldset-unique-bug
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.
--
Ticket URL: <https://code.djangoproject.com/ticket/35550>
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 view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/010701903f6c1f67-66314d54-f2bc-4822-828e-a676022cbf3c-000000%40eu-central-1.amazonses.com.