diff --git a/django/db/models/base.py b/django/db/models/base.py index 1d98ff407b..b47f4c287d 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -19,7 +19,7 @@ from django.db.models import ( NOT_PROVIDED, ExpressionWrapper, IntegerField, Max, Value, ) from django.db.models.constants import LOOKUP_SEP -from django.db.models.constraints import CheckConstraint, UniqueConstraint +from django.db.models.constraints import CheckConstraint from django.db.models.deletion import CASCADE, Collector from django.db.models.fields.related import ( ForeignObjectRel, OneToOneField, lazy_related_operation, resolve_relation, @@ -1023,12 +1023,14 @@ class Model(metaclass=ModelBase): unique_checks = [] unique_togethers = [(self.__class__, self._meta.unique_together)] - constraints = [(self.__class__, self._meta.constraints)] + constraints = [(self.__class__, self._meta.total_unique_constraints)] for parent_class in self._meta.get_parent_list(): if parent_class._meta.unique_together: unique_togethers.append((parent_class, parent_class._meta.unique_together)) - if parent_class._meta.constraints: - constraints.append((parent_class, parent_class._meta.constraints)) + if parent_class._meta.total_unique_constraints: + constraints.append( + (parent_class, parent_class._meta.total_unique_constraints) + ) for model_class, unique_together in unique_togethers: for check in unique_together: @@ -1038,10 +1040,7 @@ class Model(metaclass=ModelBase): for model_class, model_constraints in constraints: for constraint in model_constraints: - if (isinstance(constraint, UniqueConstraint) and - # Partial unique constraints can't be validated. - constraint.condition is None and - not any(name in exclude for name in constraint.fields)): + if not any(name in exclude for name in constraint.fields): unique_checks.append((model_class, constraint.fields)) # These are checks for the unique_for_. diff --git a/django/db/models/options.py b/django/db/models/options.py index ae2b073df9..f027a74976 100644 --- a/django/db/models/options.py +++ b/django/db/models/options.py @@ -7,7 +7,7 @@ from django.apps import apps from django.conf import settings from django.core.exceptions import FieldDoesNotExist from django.db import connections -from django.db.models import AutoField, Manager, OrderWrt +from django.db.models import AutoField, Manager, OrderWrt, UniqueConstraint from django.db.models.query_utils import PathInfo from django.utils.datastructures import ImmutableList, OrderedSet from django.utils.functional import cached_property @@ -827,6 +827,18 @@ class Options: self._get_fields_cache[cache_key] = fields return fields + @cached_property + def total_unique_constraints(self): + """ + Return a list of total unique constraints. Useful for determining set + of fields guaranteed to be unique for all rows. + """ + return [ + constraint + for constraint in self.constraints + if isinstance(constraint, UniqueConstraint) and constraint.condition is None + ] + @cached_property def _property_names(self): """Return a set of the names of the properties defined on the model."""