From f1aa58479cdc6051dd2e97feca2d584c43aee1e7 Mon Sep 17 00:00:00 2001 From: hui shang Date: Thu, 28 Dec 2017 07:56:24 +0800 Subject: [PATCH] Fixed #28714 -- Added system checks for invalid model field names in Meta.indexes. Thanks Gabriel for the report and Adam Johnson for the review. --- django/db/models/base.py | 7 +++ docs/ref/checks.txt | 6 +-- tests/invalid_models_tests/test_models.py | 53 +++++++++++++++++++++++ 3 files changed, 63 insertions(+), 3 deletions(-) diff --git a/django/db/models/base.py b/django/db/models/base.py index 36be70beb5..2588ee3ff6 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -1203,6 +1203,7 @@ class Model(metaclass=ModelBase): errors += [ *cls._check_index_together(), *cls._check_unique_together(), + *cls._check_indexes(), *cls._check_ordering(), ] @@ -1474,6 +1475,12 @@ class Model(metaclass=ModelBase): errors.extend(cls._check_local_fields(fields, "unique_together")) return errors + @classmethod + def _check_indexes(cls): + """Check the fields of indexes.""" + fields = [field for index in cls._meta.indexes for field, _ in index.fields_orders] + return cls._check_local_fields(fields, 'indexes') + @classmethod def _check_local_fields(cls, fields, option): from django.db import models diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt index 9b3f1be356..db86c49ee0 100644 --- a/docs/ref/checks.txt +++ b/docs/ref/checks.txt @@ -265,16 +265,16 @@ Models * **models.E009**: All ``index_together`` elements must be lists or tuples. * **models.E010**: ``unique_together`` must be a list or tuple. * **models.E011**: All ``unique_together`` elements must be lists or tuples. -* **models.E012**: ``index_together/unique_together`` refers to the +* **models.E012**: ``indexes/index_together/unique_together`` refers to the nonexistent field ````. -* **models.E013**: ``index_together/unique_together`` refers to a +* **models.E013**: ``indexes/index_together/unique_together`` refers to a ``ManyToManyField`` ````, but ``ManyToManyField``\s are not supported for that option. * **models.E014**: ``ordering`` must be a tuple or list (even if you want to order by only one field). * **models.E015**: ``ordering`` refers to the nonexistent field ````. -* **models.E016**: ``index_together/unique_together`` refers to field +* **models.E016**: ``indexes/index_together/unique_together`` refers to field ```` which is not local to model ````. * **models.E017**: Proxy model ```` contains model fields. * **models.E018**: Autogenerated column name too long for field ````. diff --git a/tests/invalid_models_tests/test_models.py b/tests/invalid_models_tests/test_models.py index 4a3e5e3a02..71e1b83a3f 100644 --- a/tests/invalid_models_tests/test_models.py +++ b/tests/invalid_models_tests/test_models.py @@ -219,6 +219,59 @@ class UniqueTogetherTests(SimpleTestCase): ]) +@isolate_apps('invalid_models_tests') +class IndexesTests(SimpleTestCase): + + def test_pointing_to_missing_field(self): + class Model(models.Model): + class Meta: + indexes = [models.Index(fields=['missing_field'], name='name')] + + self.assertEqual(Model.check(), [ + Error( + "'indexes' refers to the nonexistent field 'missing_field'.", + obj=Model, + id='models.E012', + ), + ]) + + def test_pointing_to_m2m_field(self): + class Model(models.Model): + m2m = models.ManyToManyField('self') + + class Meta: + indexes = [models.Index(fields=['m2m'], name='name')] + + self.assertEqual(Model.check(), [ + Error( + "'indexes' refers to a ManyToManyField 'm2m', but " + "ManyToManyFields are not permitted in 'indexes'.", + obj=Model, + id='models.E013', + ), + ]) + + def test_pointing_to_non_local_field(self): + class Foo(models.Model): + field1 = models.IntegerField() + + class Bar(Foo): + field2 = models.IntegerField() + + class Meta: + indexes = [models.Index(fields=['field2', 'field1'], name='name')] + + self.assertEqual(Bar.check(), [ + Error( + "'indexes' refers to field 'field1' which is not local to " + "model 'Bar'.", + hint='This issue may be caused by multi-table inheritance.', + obj=Bar, + id='models.E016', + ), + ]) + + @isolate_apps('invalid_models_tests') class FieldNamesTests(SimpleTestCase):