[Django] #35550: UniqueConstraint with condition seems not checked in BaseInlineFormSet

5 views
Skip to first unread message

Django

unread,
Jun 22, 2024, 6:09:33 AM6/22/24
to django-...@googlegroups.com
#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.

Django

unread,
Jun 23, 2024, 2:11:14 PM6/23/24
to django-...@googlegroups.com
#35550: UniqueConstraint with condition seems not checked in BaseInlineFormSet
------------------------+--------------------------------------
Reporter: Serl | Owner: nobody
Type: Bug | Status: closed
Component: Forms | Version: 5.0
Severity: Normal | Resolution: duplicate
Keywords: | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------+--------------------------------------
Changes (by Mariusz Felisiak):

* resolution: => duplicate
* status: new => closed

Comment:

Thanks for the ticket. `UniqueConstraint` with `expressions` and partial
unique constraints are ignored on purpose, see #33335.

Duplicate of #23964.
--
Ticket URL: <https://code.djangoproject.com/ticket/35550#comment:1>
Reply all
Reply to author
Forward
0 new messages