mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Fixed #31185 -- Fixed detecting of unique fields in ForeignKey/ForeignObject checks when using Meta.constraints.
This commit is contained in:
		| @@ -528,6 +528,10 @@ class ForeignObject(RelatedField): | ||||
|             frozenset(ut) | ||||
|             for ut in self.remote_field.model._meta.unique_together | ||||
|         }) | ||||
|         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) | ||||
|  | ||||
| @@ -541,8 +545,10 @@ class ForeignObject(RelatedField): | ||||
|                     "No subset of the fields %s on model '%s' is unique." | ||||
|                     % (field_combination, model_name), | ||||
|                     hint=( | ||||
|                         "Add unique=True on any of those fields or add at " | ||||
|                         "least a subset of them to a unique_together constraint." | ||||
|                         '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', | ||||
| @@ -553,8 +559,13 @@ class ForeignObject(RelatedField): | ||||
|             model_name = self.remote_field.model.__name__ | ||||
|             return [ | ||||
|                 checks.Error( | ||||
|                     "'%s.%s' must set unique=True because it is referenced by " | ||||
|                     "'%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', | ||||
|                 ) | ||||
|   | ||||
| @@ -220,7 +220,7 @@ Related fields | ||||
|   ``'__'``. | ||||
| * **fields.E310**: No subset of the fields ``<field1>``, ``<field2>``, ... on | ||||
|   model ``<model>`` is unique. | ||||
| * **fields.E311**: ``<model>`` must set ``unique=True`` because it is | ||||
| * **fields.E311**: ``<model>.<field name>`` must be unique because it is | ||||
|   referenced by a ``ForeignKey``. | ||||
| * **fields.E312**: The ``to_field`` ``<field name>`` doesn't exist on the | ||||
|   related model ``<app label>.<model>``. | ||||
|   | ||||
| @@ -352,7 +352,11 @@ class RelativeFieldTests(SimpleTestCase): | ||||
|         field = Model._meta.get_field('foreign_key') | ||||
|         self.assertEqual(field.check(), [ | ||||
|             Error( | ||||
|                 "'Target.bad' must set unique=True because it is referenced by a foreign key.", | ||||
|                 "'Target.bad' 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=field, | ||||
|                 id='fields.E311', | ||||
|             ), | ||||
| @@ -368,12 +372,64 @@ class RelativeFieldTests(SimpleTestCase): | ||||
|         field = Model._meta.get_field('field') | ||||
|         self.assertEqual(field.check(), [ | ||||
|             Error( | ||||
|                 "'Target.bad' must set unique=True because it is referenced by a foreign key.", | ||||
|                 "'Target.bad' 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=field, | ||||
|                 id='fields.E311', | ||||
|             ), | ||||
|         ]) | ||||
|  | ||||
|     def test_foreign_key_to_partially_unique_field(self): | ||||
|         class Target(models.Model): | ||||
|             source = models.IntegerField() | ||||
|  | ||||
|             class Meta: | ||||
|                 constraints = [ | ||||
|                     models.UniqueConstraint( | ||||
|                         fields=['source'], | ||||
|                         name='tfktpuf_partial_unique', | ||||
|                         condition=models.Q(pk__gt=2), | ||||
|                     ), | ||||
|                 ] | ||||
|  | ||||
|         class Model(models.Model): | ||||
|             field = models.ForeignKey(Target, models.CASCADE, to_field='source') | ||||
|  | ||||
|         field = Model._meta.get_field('field') | ||||
|         self.assertEqual(field.check(), [ | ||||
|             Error( | ||||
|                 "'Target.source' 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=field, | ||||
|                 id='fields.E311', | ||||
|             ), | ||||
|         ]) | ||||
|  | ||||
|     def test_foreign_key_to_unique_field_with_meta_constraint(self): | ||||
|         class Target(models.Model): | ||||
|             source = models.IntegerField() | ||||
|  | ||||
|             class Meta: | ||||
|                 constraints = [ | ||||
|                     models.UniqueConstraint( | ||||
|                         fields=['source'], | ||||
|                         name='tfktufwmc_unique', | ||||
|                     ), | ||||
|                 ] | ||||
|  | ||||
|         class Model(models.Model): | ||||
|             field = models.ForeignKey(Target, models.CASCADE, to_field='source') | ||||
|  | ||||
|         field = Model._meta.get_field('field') | ||||
|         self.assertEqual(field.check(), []) | ||||
|  | ||||
|     def test_foreign_object_to_non_unique_fields(self): | ||||
|         class Person(models.Model): | ||||
|             # Note that both fields are not unique. | ||||
| @@ -396,14 +452,82 @@ class RelativeFieldTests(SimpleTestCase): | ||||
|             Error( | ||||
|                 "No subset of the fields 'country_id', 'city_id' on model 'Person' is unique.", | ||||
|                 hint=( | ||||
|                     "Add unique=True on any of those fields or add at least " | ||||
|                     "a subset of them to a unique_together constraint." | ||||
|                     '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=field, | ||||
|                 id='fields.E310', | ||||
|             ) | ||||
|         ]) | ||||
|  | ||||
|     def test_foreign_object_to_partially_unique_field(self): | ||||
|         class Person(models.Model): | ||||
|             country_id = models.IntegerField() | ||||
|             city_id = models.IntegerField() | ||||
|  | ||||
|             class Meta: | ||||
|                 constraints = [ | ||||
|                     models.UniqueConstraint( | ||||
|                         fields=['country_id', 'city_id'], | ||||
|                         name='tfotpuf_partial_unique', | ||||
|                         condition=models.Q(pk__gt=2), | ||||
|                     ), | ||||
|                 ] | ||||
|  | ||||
|         class MMembership(models.Model): | ||||
|             person_country_id = models.IntegerField() | ||||
|             person_city_id = models.IntegerField() | ||||
|             person = models.ForeignObject( | ||||
|                 Person, | ||||
|                 on_delete=models.CASCADE, | ||||
|                 from_fields=['person_country_id', 'person_city_id'], | ||||
|                 to_fields=['country_id', 'city_id'], | ||||
|             ) | ||||
|  | ||||
|         field = MMembership._meta.get_field('person') | ||||
|         self.assertEqual(field.check(), [ | ||||
|             Error( | ||||
|                 "No subset of the fields 'country_id', 'city_id' on model " | ||||
|                 "'Person' 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=field, | ||||
|                 id='fields.E310', | ||||
|             ), | ||||
|         ]) | ||||
|  | ||||
|     def test_foreign_object_to_unique_field_with_meta_constraint(self): | ||||
|         class Person(models.Model): | ||||
|             country_id = models.IntegerField() | ||||
|             city_id = models.IntegerField() | ||||
|  | ||||
|             class Meta: | ||||
|                 constraints = [ | ||||
|                     models.UniqueConstraint( | ||||
|                         fields=['country_id', 'city_id'], | ||||
|                         name='tfotpuf_unique', | ||||
|                     ), | ||||
|                 ] | ||||
|  | ||||
|         class MMembership(models.Model): | ||||
|             person_country_id = models.IntegerField() | ||||
|             person_city_id = models.IntegerField() | ||||
|             person = models.ForeignObject( | ||||
|                 Person, | ||||
|                 on_delete=models.CASCADE, | ||||
|                 from_fields=['person_country_id', 'person_city_id'], | ||||
|                 to_fields=['country_id', 'city_id'], | ||||
|             ) | ||||
|  | ||||
|         field = MMembership._meta.get_field('person') | ||||
|         self.assertEqual(field.check(), []) | ||||
|  | ||||
|     def test_on_delete_set_null_on_non_nullable_field(self): | ||||
|         class Person(models.Model): | ||||
|             pass | ||||
| @@ -1453,8 +1577,10 @@ class M2mThroughFieldsTests(SimpleTestCase): | ||||
|             Error( | ||||
|                 "No subset of the fields 'a', 'b' on model 'Parent' is unique.", | ||||
|                 hint=( | ||||
|                     "Add unique=True on any of those fields or add at least " | ||||
|                     "a subset of them to a unique_together constraint." | ||||
|                     '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=field, | ||||
|                 id='fields.E310', | ||||
| @@ -1489,8 +1615,10 @@ class M2mThroughFieldsTests(SimpleTestCase): | ||||
|             Error( | ||||
|                 "No subset of the fields 'a', 'b', 'd' on model 'Parent' is unique.", | ||||
|                 hint=( | ||||
|                     "Add unique=True on any of those fields or add at least " | ||||
|                     "a subset of them to a unique_together constraint." | ||||
|                     '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=field, | ||||
|                 id='fields.E310', | ||||
|   | ||||
		Reference in New Issue
	
	Block a user