from unittest import mock from django.core import checks from django.core.checks import Error, Warning from django.db import models from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature from django.test.utils import ( isolate_apps, modify_settings, override_settings, override_system_checks, ) class EmptyRouter: pass @isolate_apps("check_framework", attr_name="apps") @override_system_checks([checks.model_checks.check_all_models]) class DuplicateDBTableTests(SimpleTestCase): def test_collision_in_same_app(self): class Model1(models.Model): class Meta: db_table = "test_table" class Model2(models.Model): class Meta: db_table = "test_table" self.assertEqual( checks.run_checks(app_configs=self.apps.get_app_configs()), [ Error( "db_table 'test_table' is used by multiple models: " "check_framework.Model1, check_framework.Model2.", obj="test_table", id="models.E028", ) ], ) @override_settings( DATABASE_ROUTERS=["check_framework.test_model_checks.EmptyRouter"] ) def test_collision_in_same_app_database_routers_installed(self): class Model1(models.Model): class Meta: db_table = "test_table" class Model2(models.Model): class Meta: db_table = "test_table" self.assertEqual( checks.run_checks(app_configs=self.apps.get_app_configs()), [ Warning( "db_table 'test_table' is used by multiple models: " "check_framework.Model1, check_framework.Model2.", hint=( "You have configured settings.DATABASE_ROUTERS. Verify " "that check_framework.Model1, check_framework.Model2 are " "correctly routed to separate databases." ), obj="test_table", id="models.W035", ) ], ) @modify_settings(INSTALLED_APPS={"append": "basic"}) @isolate_apps("basic", "check_framework", kwarg_name="apps") def test_collision_across_apps(self, apps): class Model1(models.Model): class Meta: app_label = "basic" db_table = "test_table" class Model2(models.Model): class Meta: app_label = "check_framework" db_table = "test_table" self.assertEqual( checks.run_checks(app_configs=apps.get_app_configs()), [ Error( "db_table 'test_table' is used by multiple models: " "basic.Model1, check_framework.Model2.", obj="test_table", id="models.E028", ) ], ) @modify_settings(INSTALLED_APPS={"append": "basic"}) @override_settings( DATABASE_ROUTERS=["check_framework.test_model_checks.EmptyRouter"] ) @isolate_apps("basic", "check_framework", kwarg_name="apps") def test_collision_across_apps_database_routers_installed(self, apps): class Model1(models.Model): class Meta: app_label = "basic" db_table = "test_table" class Model2(models.Model): class Meta: app_label = "check_framework" db_table = "test_table" self.assertEqual( checks.run_checks(app_configs=apps.get_app_configs()), [ Warning( "db_table 'test_table' is used by multiple models: " "basic.Model1, check_framework.Model2.", hint=( "You have configured settings.DATABASE_ROUTERS. Verify " "that basic.Model1, check_framework.Model2 are correctly " "routed to separate databases." ), obj="test_table", id="models.W035", ) ], ) def test_no_collision_for_unmanaged_models(self): class Unmanaged(models.Model): class Meta: db_table = "test_table" managed = False class Managed(models.Model): class Meta: db_table = "test_table" self.assertEqual(checks.run_checks(app_configs=self.apps.get_app_configs()), []) def test_no_collision_for_proxy_models(self): class Model(models.Model): class Meta: db_table = "test_table" class ProxyModel(Model): class Meta: proxy = True self.assertEqual(Model._meta.db_table, ProxyModel._meta.db_table) self.assertEqual(checks.run_checks(app_configs=self.apps.get_app_configs()), []) @isolate_apps("check_framework", attr_name="apps") @override_system_checks([checks.model_checks.check_all_models]) class IndexNameTests(SimpleTestCase): def test_collision_in_same_model(self): index = models.Index(fields=["id"], name="foo") class Model(models.Model): class Meta: indexes = [index, index] self.assertEqual( checks.run_checks(app_configs=self.apps.get_app_configs()), [ Error( "index name 'foo' is not unique for model check_framework.Model.", id="models.E029", ), ], ) def test_collision_in_different_models(self): index = models.Index(fields=["id"], name="foo") class Model1(models.Model): class Meta: indexes = [index] class Model2(models.Model): class Meta: indexes = [index] self.assertEqual( checks.run_checks(app_configs=self.apps.get_app_configs()), [ Error( "index name 'foo' is not unique among models: " "check_framework.Model1, check_framework.Model2.", id="models.E030", ), ], ) def test_collision_abstract_model(self): class AbstractModel(models.Model): class Meta: indexes = [models.Index(fields=["id"], name="foo")] abstract = True class Model1(AbstractModel): pass class Model2(AbstractModel): pass self.assertEqual( checks.run_checks(app_configs=self.apps.get_app_configs()), [ Error( "index name 'foo' is not unique among models: " "check_framework.Model1, check_framework.Model2.", id="models.E030", ), ], ) def test_no_collision_abstract_model_interpolation(self): class AbstractModel(models.Model): name = models.CharField(max_length=20) class Meta: indexes = [ models.Index(fields=["name"], name="%(app_label)s_%(class)s_foo") ] abstract = True class Model1(AbstractModel): pass class Model2(AbstractModel): pass self.assertEqual(checks.run_checks(app_configs=self.apps.get_app_configs()), []) @modify_settings(INSTALLED_APPS={"append": "basic"}) @isolate_apps("basic", "check_framework", kwarg_name="apps") def test_collision_across_apps(self, apps): index = models.Index(fields=["id"], name="foo") class Model1(models.Model): class Meta: app_label = "basic" indexes = [index] class Model2(models.Model): class Meta: app_label = "check_framework" indexes = [index] self.assertEqual( checks.run_checks(app_configs=apps.get_app_configs()), [ Error( "index name 'foo' is not unique among models: basic.Model1, " "check_framework.Model2.", id="models.E030", ), ], ) @modify_settings(INSTALLED_APPS={"append": "basic"}) @isolate_apps("basic", "check_framework", kwarg_name="apps") def test_no_collision_across_apps_interpolation(self, apps): index = models.Index(fields=["id"], name="%(app_label)s_%(class)s_foo") class Model1(models.Model): class Meta: app_label = "basic" constraints = [index] class Model2(models.Model): class Meta: app_label = "check_framework" constraints = [index] self.assertEqual(checks.run_checks(app_configs=apps.get_app_configs()), []) @isolate_apps("check_framework", attr_name="apps") @override_system_checks([checks.model_checks.check_all_models]) @skipUnlessDBFeature("supports_table_check_constraints") class ConstraintNameTests(TestCase): def test_collision_in_same_model(self): class Model(models.Model): class Meta: constraints = [ models.CheckConstraint(check=models.Q(id__gt=0), name="foo"), models.CheckConstraint(check=models.Q(id__lt=100), name="foo"), ] self.assertEqual( checks.run_checks(app_configs=self.apps.get_app_configs()), [ Error( "constraint name 'foo' is not unique for model " "check_framework.Model.", id="models.E031", ), ], ) def test_collision_in_different_models(self): constraint = models.CheckConstraint(check=models.Q(id__gt=0), name="foo") class Model1(models.Model): class Meta: constraints = [constraint] class Model2(models.Model): class Meta: constraints = [constraint] self.assertEqual( checks.run_checks(app_configs=self.apps.get_app_configs()), [ Error( "constraint name 'foo' is not unique among models: " "check_framework.Model1, check_framework.Model2.", id="models.E032", ), ], ) def test_collision_abstract_model(self): class AbstractModel(models.Model): class Meta: constraints = [ models.CheckConstraint(check=models.Q(id__gt=0), name="foo") ] abstract = True class Model1(AbstractModel): pass class Model2(AbstractModel): pass self.assertEqual( checks.run_checks(app_configs=self.apps.get_app_configs()), [ Error( "constraint name 'foo' is not unique among models: " "check_framework.Model1, check_framework.Model2.", id="models.E032", ), ], ) def test_no_collision_abstract_model_interpolation(self): class AbstractModel(models.Model): class Meta: constraints = [ models.CheckConstraint( check=models.Q(id__gt=0), name="%(app_label)s_%(class)s_foo" ), ] abstract = True class Model1(AbstractModel): pass class Model2(AbstractModel): pass self.assertEqual(checks.run_checks(app_configs=self.apps.get_app_configs()), []) @modify_settings(INSTALLED_APPS={"append": "basic"}) @isolate_apps("basic", "check_framework", kwarg_name="apps") def test_collision_across_apps(self, apps): constraint = models.CheckConstraint(check=models.Q(id__gt=0), name="foo") class Model1(models.Model): class Meta: app_label = "basic" constraints = [constraint] class Model2(models.Model): class Meta: app_label = "check_framework" constraints = [constraint] self.assertEqual( checks.run_checks(app_configs=apps.get_app_configs()), [ Error( "constraint name 'foo' is not unique among models: " "basic.Model1, check_framework.Model2.", id="models.E032", ), ], ) @modify_settings(INSTALLED_APPS={"append": "basic"}) @isolate_apps("basic", "check_framework", kwarg_name="apps") def test_no_collision_across_apps_interpolation(self, apps): constraint = models.CheckConstraint( check=models.Q(id__gt=0), name="%(app_label)s_%(class)s_foo" ) class Model1(models.Model): class Meta: app_label = "basic" constraints = [constraint] class Model2(models.Model): class Meta: app_label = "check_framework" constraints = [constraint] self.assertEqual(checks.run_checks(app_configs=apps.get_app_configs()), []) def mocked_is_overridden(self, setting): # Force treating DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' as a not # overridden setting. return ( setting != "DEFAULT_AUTO_FIELD" or self.DEFAULT_AUTO_FIELD != "django.db.models.AutoField" ) @mock.patch("django.conf.UserSettingsHolder.is_overridden", mocked_is_overridden) @override_settings(DEFAULT_AUTO_FIELD="django.db.models.AutoField") @isolate_apps("check_framework.apps.CheckDefaultPKConfig", attr_name="apps") @override_system_checks([checks.model_checks.check_all_models]) class ModelDefaultAutoFieldTests(SimpleTestCase): msg = ( "Auto-created primary key used when not defining a primary key type, " "by default 'django.db.models.AutoField'." ) hint = ( "Configure the DEFAULT_AUTO_FIELD setting or the " "CheckDefaultPKConfig.default_auto_field attribute to point to a " "subclass of AutoField, e.g. 'django.db.models.BigAutoField'." ) def test_auto_created_pk(self): class Model(models.Model): pass self.assertEqual( checks.run_checks(app_configs=self.apps.get_app_configs()), [ Warning(self.msg, hint=self.hint, obj=Model, id="models.W042"), ], ) def test_explicit_inherited_pk(self): class Parent(models.Model): id = models.AutoField(primary_key=True) class Child(Parent): pass self.assertEqual(checks.run_checks(app_configs=self.apps.get_app_configs()), []) def test_skipped_on_model_with_invalid_app_label(self): class Model(models.Model): class Meta: app_label = "invalid_app_label" self.assertEqual(Model.check(), []) def test_skipped_on_abstract_model(self): class Abstract(models.Model): class Meta: abstract = True # Call .check() because abstract models are not registered. self.assertEqual(Abstract.check(), []) def test_explicit_inherited_parent_link(self): class Parent(models.Model): id = models.AutoField(primary_key=True) class Child(Parent): parent_ptr = models.OneToOneField(Parent, models.CASCADE, parent_link=True) self.assertEqual(checks.run_checks(app_configs=self.apps.get_app_configs()), []) def test_auto_created_inherited_pk(self): class Parent(models.Model): pass class Child(Parent): pass self.assertEqual( checks.run_checks(app_configs=self.apps.get_app_configs()), [ Warning(self.msg, hint=self.hint, obj=Parent, id="models.W042"), ], ) def test_auto_created_inherited_parent_link(self): class Parent(models.Model): pass class Child(Parent): parent_ptr = models.OneToOneField(Parent, models.CASCADE, parent_link=True) self.assertEqual( checks.run_checks(app_configs=self.apps.get_app_configs()), [ Warning(self.msg, hint=self.hint, obj=Parent, id="models.W042"), ], ) def test_auto_created_pk_inherited_abstract_parent(self): class Parent(models.Model): class Meta: abstract = True class Child(Parent): pass self.assertEqual( checks.run_checks(app_configs=self.apps.get_app_configs()), [ Warning(self.msg, hint=self.hint, obj=Child, id="models.W042"), ], ) @override_settings(DEFAULT_AUTO_FIELD="django.db.models.BigAutoField") def test_default_auto_field_setting(self): class Model(models.Model): pass self.assertEqual(checks.run_checks(app_configs=self.apps.get_app_configs()), []) def test_explicit_pk(self): class Model(models.Model): id = models.BigAutoField(primary_key=True) self.assertEqual(checks.run_checks(app_configs=self.apps.get_app_configs()), []) @isolate_apps("check_framework.apps.CheckPKConfig", kwarg_name="apps") def test_app_default_auto_field(self, apps): class ModelWithPkViaAppConfig(models.Model): class Meta: app_label = "check_framework.apps.CheckPKConfig" self.assertEqual(checks.run_checks(app_configs=apps.get_app_configs()), [])