diff options
| author | Tom Forbes <tom@tomforb.es> | 2020-07-12 13:59:57 +0100 |
|---|---|---|
| committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2020-12-15 11:25:46 +0100 |
| commit | b5e12d490af3debca8c55ab3c1698189fdedbbdb (patch) | |
| tree | 5fe3005ac567f3addf78b81ae033191e2fa642f4 /tests | |
| parent | b960e4ed722a04a9db0d35293f76e253eedf9126 (diff) | |
Fixed #31007 -- Allowed specifying type of auto-created primary keys.
This also changes the default type of auto-created primary keys
for new apps and projects to BigAutoField.
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/admin_scripts/tests.py | 15 | ||||
| -rw-r--r-- | tests/apps/apps.py | 5 | ||||
| -rw-r--r-- | tests/apps/tests.py | 28 | ||||
| -rw-r--r-- | tests/check_framework/apps.py | 10 | ||||
| -rw-r--r-- | tests/check_framework/test_model_checks.py | 57 | ||||
| -rw-r--r-- | tests/model_options/apps.py | 25 | ||||
| -rw-r--r-- | tests/model_options/test_default_pk.py | 101 | ||||
| -rw-r--r-- | tests/test_sqlite.py | 2 |
8 files changed, 241 insertions, 2 deletions
diff --git a/tests/admin_scripts/tests.py b/tests/admin_scripts/tests.py index a82f5be5ed..7a38306d17 100644 --- a/tests/admin_scripts/tests.py +++ b/tests/admin_scripts/tests.py @@ -61,6 +61,7 @@ class AdminScriptTestCase(SimpleTestCase): settings_file.write("%s\n" % extra) exports = [ 'DATABASES', + 'DEFAULT_AUTO_FIELD', 'ROOT_URLCONF', 'SECRET_KEY', ] @@ -2188,6 +2189,20 @@ class StartApp(AdminScriptTestCase): "won't replace conflicting files." ) + def test_template(self): + out, err = self.run_django_admin(['startapp', 'new_app']) + self.assertNoOutput(err) + app_path = os.path.join(self.test_dir, 'new_app') + self.assertIs(os.path.exists(app_path), True) + with open(os.path.join(app_path, 'apps.py')) as f: + content = f.read() + self.assertIn('class NewAppConfig(AppConfig)', content) + self.assertIn( + "default_auto_field = 'django.db.models.BigAutoField'", + content, + ) + self.assertIn("name = 'new_app'", content) + class DiffSettings(AdminScriptTestCase): """Tests for diffsettings management command.""" diff --git a/tests/apps/apps.py b/tests/apps/apps.py index d322b28f2b..efd2983779 100644 --- a/tests/apps/apps.py +++ b/tests/apps/apps.py @@ -31,3 +31,8 @@ class PlainAppsConfig(AppConfig): class RelabeledAppsConfig(AppConfig): name = 'apps' label = 'relabeled' + + +class ModelPKAppsConfig(AppConfig): + name = 'apps' + default_auto_field = 'django.db.models.BigAutoField' diff --git a/tests/apps/tests.py b/tests/apps/tests.py index 26d135f244..0e1f918bcc 100644 --- a/tests/apps/tests.py +++ b/tests/apps/tests.py @@ -102,8 +102,8 @@ class AppsTests(SimpleTestCase): def test_no_such_app_config_with_choices(self): msg = ( "Module 'apps.apps' does not contain a 'NoSuchConfig' class. " - "Choices are: 'BadConfig', 'MyAdmin', 'MyAuth', 'NoSuchApp', " - "'PlainAppsConfig', 'RelabeledAppsConfig'." + "Choices are: 'BadConfig', 'ModelPKAppsConfig', 'MyAdmin', " + "'MyAuth', 'NoSuchApp', 'PlainAppsConfig', 'RelabeledAppsConfig'." ) with self.assertRaisesMessage(ImportError, msg): with self.settings(INSTALLED_APPS=['apps.apps.NoSuchConfig']): @@ -436,6 +436,30 @@ class AppConfigTests(SimpleTestCase): ac = AppConfig('label', Stub(__path__=['a'])) self.assertEqual(repr(ac), '<AppConfig: label>') + @override_settings( + INSTALLED_APPS=['apps.apps.ModelPKAppsConfig'], + DEFAULT_AUTO_FIELD='django.db.models.SmallAutoField', + ) + def test_app_default_auto_field(self): + apps_config = apps.get_app_config('apps') + self.assertEqual( + apps_config.default_auto_field, + 'django.db.models.BigAutoField', + ) + self.assertIs(apps_config._is_default_auto_field_overridden, True) + + @override_settings( + INSTALLED_APPS=['apps.apps.PlainAppsConfig'], + DEFAULT_AUTO_FIELD='django.db.models.SmallAutoField', + ) + def test_default_auto_field_setting(self): + apps_config = apps.get_app_config('apps') + self.assertEqual( + apps_config.default_auto_field, + 'django.db.models.SmallAutoField', + ) + self.assertIs(apps_config._is_default_auto_field_overridden, False) + class NamespacePackageAppTests(SimpleTestCase): # We need nsapp to be top-level so our multiple-paths tests can add another diff --git a/tests/check_framework/apps.py b/tests/check_framework/apps.py new file mode 100644 index 0000000000..c57994f722 --- /dev/null +++ b/tests/check_framework/apps.py @@ -0,0 +1,10 @@ +from django.apps import AppConfig + + +class CheckDefaultPKConfig(AppConfig): + name = 'check_framework' + + +class CheckPKConfig(AppConfig): + name = 'check_framework' + default_auto_field = 'django.db.models.BigAutoField' diff --git a/tests/check_framework/test_model_checks.py b/tests/check_framework/test_model_checks.py index ce2d73fa20..d4342ada07 100644 --- a/tests/check_framework/test_model_checks.py +++ b/tests/check_framework/test_model_checks.py @@ -1,3 +1,5 @@ +from unittest import mock + from django.core import checks from django.core.checks import Error, Warning from django.db import models @@ -358,3 +360,58 @@ class ConstraintNameTests(TestCase): 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): + def test_auto_created_pk(self): + class Model(models.Model): + pass + + self.assertEqual(checks.run_checks(app_configs=self.apps.get_app_configs()), [ + Warning( + "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'." + ), + obj=Model, + 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()), []) diff --git a/tests/model_options/apps.py b/tests/model_options/apps.py new file mode 100644 index 0000000000..be2250fe7a --- /dev/null +++ b/tests/model_options/apps.py @@ -0,0 +1,25 @@ +from django.apps import AppConfig + + +class ModelDefaultPKConfig(AppConfig): + name = 'model_options' + + +class ModelPKConfig(AppConfig): + name = 'model_options' + default_auto_field = 'django.db.models.SmallAutoField' + + +class ModelPKNonAutoConfig(AppConfig): + name = 'model_options' + default_auto_field = 'django.db.models.TextField' + + +class ModelPKNoneConfig(AppConfig): + name = 'model_options' + default_auto_field = None + + +class ModelPKNonexistentConfig(AppConfig): + name = 'model_options' + default_auto_field = 'django.db.models.NonexistentAutoField' diff --git a/tests/model_options/test_default_pk.py b/tests/model_options/test_default_pk.py new file mode 100644 index 0000000000..b678848916 --- /dev/null +++ b/tests/model_options/test_default_pk.py @@ -0,0 +1,101 @@ +from django.core.exceptions import ImproperlyConfigured +from django.db import models +from django.test import SimpleTestCase, override_settings +from django.test.utils import isolate_apps + + +@isolate_apps('model_options') +class TestDefaultPK(SimpleTestCase): + @override_settings(DEFAULT_AUTO_FIELD='django.db.models.NonexistentAutoField') + def test_default_auto_field_setting_nonexistent(self): + msg = ( + "DEFAULT_AUTO_FIELD refers to the module " + "'django.db.models.NonexistentAutoField' that could not be " + "imported." + ) + with self.assertRaisesMessage(ImproperlyConfigured, msg): + class Model(models.Model): + pass + + @isolate_apps('model_options.apps.ModelPKNonexistentConfig') + def test_app_default_auto_field_nonexistent(self): + msg = ( + "model_options.apps.ModelPKNonexistentConfig.default_auto_field " + "refers to the module 'django.db.models.NonexistentAutoField' " + "that could not be imported." + ) + with self.assertRaisesMessage(ImproperlyConfigured, msg): + class Model(models.Model): + pass + + @override_settings(DEFAULT_AUTO_FIELD='django.db.models.TextField') + def test_default_auto_field_setting_non_auto(self): + msg = ( + "Primary key 'django.db.models.TextField' referred by " + "DEFAULT_AUTO_FIELD must subclass AutoField." + ) + with self.assertRaisesMessage(ValueError, msg): + class Model(models.Model): + pass + + @isolate_apps('model_options.apps.ModelPKNonAutoConfig') + def test_app_default_auto_field_non_auto(self): + msg = ( + "Primary key 'django.db.models.TextField' referred by " + "model_options.apps.ModelPKNonAutoConfig.default_auto_field must " + "subclass AutoField." + ) + with self.assertRaisesMessage(ValueError, msg): + class Model(models.Model): + pass + + @override_settings(DEFAULT_AUTO_FIELD=None) + def test_default_auto_field_setting_none(self): + msg = 'DEFAULT_AUTO_FIELD must not be empty.' + with self.assertRaisesMessage(ImproperlyConfigured, msg): + class Model(models.Model): + pass + + @isolate_apps('model_options.apps.ModelPKNoneConfig') + def test_app_default_auto_field_none(self): + msg = ( + 'model_options.apps.ModelPKNoneConfig.default_auto_field must not ' + 'be empty.' + ) + with self.assertRaisesMessage(ImproperlyConfigured, msg): + class Model(models.Model): + pass + + @isolate_apps('model_options.apps.ModelDefaultPKConfig') + @override_settings(DEFAULT_AUTO_FIELD='django.db.models.SmallAutoField') + def test_default_auto_field_setting(self): + class Model(models.Model): + pass + + self.assertIsInstance(Model._meta.pk, models.SmallAutoField) + + @isolate_apps('model_options.apps.ModelPKConfig') + @override_settings(DEFAULT_AUTO_FIELD='django.db.models.AutoField') + def test_app_default_auto_field(self): + class Model(models.Model): + pass + + self.assertIsInstance(Model._meta.pk, models.SmallAutoField) + + @isolate_apps('model_options.apps.ModelDefaultPKConfig') + @override_settings(DEFAULT_AUTO_FIELD='django.db.models.SmallAutoField') + def test_m2m_default_auto_field_setting(self): + class M2MModel(models.Model): + m2m = models.ManyToManyField('self') + + m2m_pk = M2MModel._meta.get_field('m2m').remote_field.through._meta.pk + self.assertIsInstance(m2m_pk, models.SmallAutoField) + + @isolate_apps('model_options.apps.ModelPKConfig') + @override_settings(DEFAULT_AUTO_FIELD='django.db.models.AutoField') + def test_m2m_app_default_auto_field(self): + class M2MModel(models.Model): + m2m = models.ManyToManyField('self') + + m2m_pk = M2MModel._meta.get_field('m2m').remote_field.through._meta.pk + self.assertIsInstance(m2m_pk, models.SmallAutoField) diff --git a/tests/test_sqlite.py b/tests/test_sqlite.py index f1b65f7d01..099f37e56d 100644 --- a/tests/test_sqlite.py +++ b/tests/test_sqlite.py @@ -27,3 +27,5 @@ SECRET_KEY = "django_tests_secret_key" PASSWORD_HASHERS = [ 'django.contrib.auth.hashers.MD5PasswordHasher', ] + +DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' |
