diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index f903dc2f22..3e4bfe34c1 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -615,60 +615,56 @@ class ForeignObject(RelatedField): if not self.foreign_related_fields: return [] - unique_foreign_fields = { - frozenset([f.name]) - for f in self.remote_field.model._meta.get_fields() - if getattr(f, "unique", False) - } - unique_foreign_fields.update( - {frozenset(ut) for ut in self.remote_field.model._meta.unique_together} + has_unique_constraint = any( + rel_field.unique for rel_field in self.foreign_related_fields ) - unique_foreign_fields.update( - { - frozenset(uc.fields) - for uc in self.remote_field.model._meta.total_unique_constraints - } - ) - foreign_fields = {f.name for f in self.foreign_related_fields} - has_unique_constraint = any(u <= foreign_fields for u in unique_foreign_fields) - - if not has_unique_constraint and len(self.foreign_related_fields) > 1: - field_combination = ", ".join( - "'%s'" % rel_field.name for rel_field in self.foreign_related_fields + if not has_unique_constraint: + foreign_fields = {f.name for f in self.foreign_related_fields} + remote_opts = self.remote_field.model._meta + has_unique_constraint = any( + frozenset(ut) <= foreign_fields for ut in remote_opts.unique_together + ) or any( + frozenset(uc.fields) <= foreign_fields + for uc in remote_opts.total_unique_constraints ) - model_name = self.remote_field.model.__name__ - return [ - checks.Error( - "No subset of the fields %s on model '%s' is unique." - % (field_combination, model_name), - hint=( - "Mark a single field as unique=True or add a set of " - "fields to a unique constraint (via unique_together " - "or a UniqueConstraint (without condition) in the " - "model Meta.constraints)." - ), - obj=self, - id="fields.E310", + + if not has_unique_constraint: + if len(self.foreign_related_fields) > 1: + field_combination = ", ".join( + f"'{rel_field.name}'" for rel_field in self.foreign_related_fields ) - ] - elif not has_unique_constraint: - field_name = self.foreign_related_fields[0].name - model_name = self.remote_field.model.__name__ - return [ - checks.Error( - "'%s.%s' must be unique because it is referenced by " - "a foreign key." % (model_name, field_name), - hint=( - "Add unique=True to this field or add a " - "UniqueConstraint (without condition) in the model " - "Meta.constraints." - ), - obj=self, - id="fields.E311", - ) - ] - else: - return [] + model_name = self.remote_field.model.__name__ + return [ + checks.Error( + f"No subset of the fields {field_combination} on model " + f"'{model_name}' is unique.", + hint=( + "Mark a single field as unique=True or add a set of " + "fields to a unique constraint (via unique_together " + "or a UniqueConstraint (without condition) in the " + "model Meta.constraints)." + ), + obj=self, + id="fields.E310", + ) + ] + else: + field_name = self.foreign_related_fields[0].name + model_name = self.remote_field.model.__name__ + return [ + checks.Error( + f"'{model_name}.{field_name}' must be unique because it is " + "referenced by a foreign key.", + hint=( + "Add unique=True to this field or add a " + "UniqueConstraint (without condition) in the model " + "Meta.constraints." + ), + obj=self, + id="fields.E311", + ) + ] + return [] def deconstruct(self): name, path, args, kwargs = super().deconstruct()