summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgabn88 <gerben_m88@hotmail.com>2024-01-11 17:43:52 +0100
committerSarah Boyce <42296566+sarahboyce@users.noreply.github.com>2024-10-10 09:20:37 +0200
commite970bb7ca71c00594b42a024a15a8ac007cc2c7a (patch)
treebc3178c664e587534a90c1b35f3d229bfc030380
parent1979b1403adeb437831a1a8885353e158ec9ad55 (diff)
Fixed #35103 -- Used provided error code and message when fields is set without a condition on UniqueConstraint.
-rw-r--r--django/db/models/constraints.py16
-rw-r--r--docs/ref/models/constraints.txt52
-rw-r--r--docs/releases/5.2.txt5
-rw-r--r--tests/constraints/models.py4
-rw-r--r--tests/constraints/tests.py35
5 files changed, 89 insertions, 23 deletions
diff --git a/django/db/models/constraints.py b/django/db/models/constraints.py
index 3859a60e2f..788e2b635b 100644
--- a/django/db/models/constraints.py
+++ b/django/db/models/constraints.py
@@ -690,11 +690,19 @@ class UniqueConstraint(BaseConstraint):
queryset = queryset.exclude(pk=model_class_pk)
if not self.condition:
if queryset.exists():
- if self.fields:
- # When fields are defined, use the unique_error_message() for
- # backward compatibility.
+ if (
+ self.fields
+ and self.violation_error_message
+ == self.default_violation_error_message
+ ):
+ # When fields are defined, use the unique_error_message() as
+ # a default for backward compatibility.
+ validation_error_message = instance.unique_error_message(
+ model, self.fields
+ )
raise ValidationError(
- instance.unique_error_message(model, self.fields),
+ validation_error_message,
+ code=validation_error_message.code,
)
raise ValidationError(
self.get_violation_error_message(),
diff --git a/docs/ref/models/constraints.txt b/docs/ref/models/constraints.txt
index 7dfc3b7d28..c1f140c265 100644
--- a/docs/ref/models/constraints.txt
+++ b/docs/ref/models/constraints.txt
@@ -282,27 +282,47 @@ PostgreSQL 15+.
.. attribute:: UniqueConstraint.violation_error_code
-The error code used when ``ValidationError`` is raised during
-:ref:`model validation <validating-objects>`. Defaults to ``None``.
+The error code used when a ``ValidationError`` is raised during
+:ref:`model validation <validating-objects>`.
+
+Defaults to :attr:`.BaseConstraint.violation_error_code`, when either
+:attr:`.UniqueConstraint.condition` is set or :attr:`.UniqueConstraint.fields`
+is not set.
+
+If :attr:`.UniqueConstraint.fields` is set without a
+:attr:`.UniqueConstraint.condition`, defaults to the :attr:`Meta.unique_together
+<django.db.models.Options.unique_together>` error code when there are multiple
+fields, and to the :attr:`.Field.unique` error code when there is a single
+field.
+
+.. versionchanged:: 5.2
-This code is *not used* for :class:`UniqueConstraint`\s with
-:attr:`~UniqueConstraint.fields` and without a
-:attr:`~UniqueConstraint.condition`. Such :class:`~UniqueConstraint`\s have the
-same error code as constraints defined with :attr:`.Field.unique` or in
-:attr:`Meta.unique_together <django.db.models.Options.constraints>`.
+ In older versions, the provided
+ :attr:`.UniqueConstraint.violation_error_code` was not used when
+ :attr:`.UniqueConstraint.fields` was set without a
+ :attr:`.UniqueConstraint.condition`.
``violation_error_message``
---------------------------
.. attribute:: UniqueConstraint.violation_error_message
-The error message used when ``ValidationError`` is raised during
-:ref:`model validation <validating-objects>`. Defaults to
-:attr:`.BaseConstraint.violation_error_message`.
+The error message used when a ``ValidationError`` is raised during
+:ref:`model validation <validating-objects>`.
+
+Defaults to :attr:`.BaseConstraint.violation_error_message`, when either
+:attr:`.UniqueConstraint.condition` is set or :attr:`.UniqueConstraint.fields`
+is not set.
+
+If :attr:`.UniqueConstraint.fields` is set without a
+:attr:`.UniqueConstraint.condition`, defaults to the :attr:`Meta.unique_together
+<django.db.models.Options.unique_together>` error message when there are
+multiple fields, and to the :attr:`.Field.unique` error message when there is a
+single field.
+
+.. versionchanged:: 5.2
-This message is *not used* for :class:`UniqueConstraint`\s with
-:attr:`~UniqueConstraint.fields` and without a
-:attr:`~UniqueConstraint.condition`. Such :class:`~UniqueConstraint`\s show the
-same message as constraints defined with
-:attr:`.Field.unique` or in
-:attr:`Meta.unique_together <django.db.models.Options.constraints>`.
+ In older versions, the provided
+ :attr:`.UniqueConstraint.violation_error_message` was not used when
+ :attr:`.UniqueConstraint.fields` was set without a
+ :attr:`.UniqueConstraint.condition`.
diff --git a/docs/releases/5.2.txt b/docs/releases/5.2.txt
index 5c5020a3f4..eabc27c277 100644
--- a/docs/releases/5.2.txt
+++ b/docs/releases/5.2.txt
@@ -358,6 +358,11 @@ Miscellaneous
* ``HttpRequest.accepted_types`` is now sorted by the client's preference, based
on the request's ``Accept`` header.
+* :attr:`.UniqueConstraint.violation_error_code` and
+ :attr:`.UniqueConstraint.violation_error_message` are now always used when
+ provided. Previously, these were ignored when :attr:`.UniqueConstraint.fields`
+ were set without a :attr:`.UniqueConstraint.condition`.
+
* The :func:`~django.template.context_processors.debug` context processor is no
longer included in the default project template.
diff --git a/tests/constraints/models.py b/tests/constraints/models.py
index 829f671cdd..95a29ffa4d 100644
--- a/tests/constraints/models.py
+++ b/tests/constraints/models.py
@@ -72,15 +72,13 @@ class GeneratedFieldVirtualProduct(models.Model):
class UniqueConstraintProduct(models.Model):
name = models.CharField(max_length=255)
color = models.CharField(max_length=32, null=True)
+ age = models.IntegerField(null=True)
class Meta:
constraints = [
models.UniqueConstraint(
fields=["name", "color"],
name="name_color_uniq",
- # Custom message and error code are ignored.
- violation_error_code="custom_code",
- violation_error_message="Custom message",
)
]
diff --git a/tests/constraints/tests.py b/tests/constraints/tests.py
index e1c431956f..9047710098 100644
--- a/tests/constraints/tests.py
+++ b/tests/constraints/tests.py
@@ -953,6 +953,41 @@ class UniqueConstraintTests(TestCase):
ChildUniqueConstraintProduct(name=self.p1.name, color=self.p1.color),
)
+ def test_validate_unique_custom_code_and_message(self):
+ product = UniqueConstraintProduct.objects.create(
+ name="test", color="red", age=42
+ )
+ code = "custom_code"
+ message = "Custom message"
+ multiple_fields_constraint = models.UniqueConstraint(
+ fields=["color", "age"],
+ name="color_age_uniq",
+ violation_error_code=code,
+ violation_error_message=message,
+ )
+ single_field_constraint = models.UniqueConstraint(
+ fields=["color"],
+ name="color_uniq",
+ violation_error_code=code,
+ violation_error_message=message,
+ )
+
+ with self.assertRaisesMessage(ValidationError, message) as cm:
+ multiple_fields_constraint.validate(
+ UniqueConstraintProduct,
+ UniqueConstraintProduct(
+ name="new-test", color=product.color, age=product.age
+ ),
+ )
+ self.assertEqual(cm.exception.code, code)
+
+ with self.assertRaisesMessage(ValidationError, message) as cm:
+ single_field_constraint.validate(
+ UniqueConstraintProduct,
+ UniqueConstraintProduct(name="new-test", color=product.color),
+ )
+ self.assertEqual(cm.exception.code, code)
+
@skipUnlessDBFeature("supports_table_check_constraints")
def test_validate_fields_unattached(self):
Product.objects.create(price=42)