summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorTom Forbes <tom@tomforb.es>2020-07-12 13:59:57 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2020-12-15 11:25:46 +0100
commitb5e12d490af3debca8c55ab3c1698189fdedbbdb (patch)
tree5fe3005ac567f3addf78b81ae033191e2fa642f4 /django
parentb960e4ed722a04a9db0d35293f76e253eedf9126 (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 'django')
-rw-r--r--django/apps/config.py10
-rw-r--r--django/conf/app_template/apps.py-tpl1
-rw-r--r--django/conf/global_settings.py3
-rw-r--r--django/conf/project_template/project_name/settings.py-tpl5
-rw-r--r--django/contrib/admin/apps.py1
-rw-r--r--django/contrib/auth/apps.py1
-rw-r--r--django/contrib/contenttypes/apps.py1
-rw-r--r--django/contrib/flatpages/apps.py1
-rw-r--r--django/contrib/gis/apps.py1
-rw-r--r--django/contrib/redirects/apps.py1
-rw-r--r--django/contrib/sitemaps/apps.py1
-rw-r--r--django/contrib/sites/apps.py1
-rw-r--r--django/db/models/base.py25
-rw-r--r--django/db/models/options.py37
14 files changed, 87 insertions, 2 deletions
diff --git a/django/apps/config.py b/django/apps/config.py
index 928e19ac44..6d794eee3a 100644
--- a/django/apps/config.py
+++ b/django/apps/config.py
@@ -5,6 +5,7 @@ from importlib import import_module
from django.core.exceptions import ImproperlyConfigured
from django.utils.deprecation import RemovedInDjango41Warning
+from django.utils.functional import cached_property
from django.utils.module_loading import import_string, module_has_submodule
APPS_MODULE_NAME = 'apps'
@@ -55,6 +56,15 @@ class AppConfig:
def __repr__(self):
return '<%s: %s>' % (self.__class__.__name__, self.label)
+ @cached_property
+ def default_auto_field(self):
+ from django.conf import settings
+ return settings.DEFAULT_AUTO_FIELD
+
+ @property
+ def _is_default_auto_field_overridden(self):
+ return self.__class__.default_auto_field is not AppConfig.default_auto_field
+
def _path_from_module(self, module):
"""Attempt to determine app's filesystem path from its module."""
# See #21874 for extended discussion of the behavior of this method in
diff --git a/django/conf/app_template/apps.py-tpl b/django/conf/app_template/apps.py-tpl
index 9b2ce5289c..b705352181 100644
--- a/django/conf/app_template/apps.py-tpl
+++ b/django/conf/app_template/apps.py-tpl
@@ -2,4 +2,5 @@ from django.apps import AppConfig
class {{ camel_case_app_name }}Config(AppConfig):
+ default_auto_field = 'django.db.models.BigAutoField'
name = '{{ app_name }}'
diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py
index 381ad63ae6..cf9fae496e 100644
--- a/django/conf/global_settings.py
+++ b/django/conf/global_settings.py
@@ -414,6 +414,9 @@ THOUSAND_SEPARATOR = ','
DEFAULT_TABLESPACE = ''
DEFAULT_INDEX_TABLESPACE = ''
+# Default primary key field type.
+DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
+
# Default X-Frame-Options header value
X_FRAME_OPTIONS = 'DENY'
diff --git a/django/conf/project_template/project_name/settings.py-tpl b/django/conf/project_template/project_name/settings.py-tpl
index 444c899b2b..7830fb2f3c 100644
--- a/django/conf/project_template/project_name/settings.py-tpl
+++ b/django/conf/project_template/project_name/settings.py-tpl
@@ -118,3 +118,8 @@ USE_TZ = True
# https://docs.djangoproject.com/en/{{ docs_version }}/howto/static-files/
STATIC_URL = '/static/'
+
+# Default primary key field type
+# https://docs.djangoproject.com/en/{{ docs_version }}/ref/settings/#default-auto-field
+
+DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
diff --git a/django/contrib/admin/apps.py b/django/contrib/admin/apps.py
index 85e9ff830a..c4fba8837c 100644
--- a/django/contrib/admin/apps.py
+++ b/django/contrib/admin/apps.py
@@ -7,6 +7,7 @@ from django.utils.translation import gettext_lazy as _
class SimpleAdminConfig(AppConfig):
"""Simple AppConfig which does not do automatic discovery."""
+ default_auto_field = 'django.db.models.AutoField'
default_site = 'django.contrib.admin.sites.AdminSite'
name = 'django.contrib.admin'
verbose_name = _("Administration")
diff --git a/django/contrib/auth/apps.py b/django/contrib/auth/apps.py
index b9d271bb1f..4e4ef06d27 100644
--- a/django/contrib/auth/apps.py
+++ b/django/contrib/auth/apps.py
@@ -11,6 +11,7 @@ from .signals import user_logged_in
class AuthConfig(AppConfig):
+ default_auto_field = 'django.db.models.AutoField'
name = 'django.contrib.auth'
verbose_name = _("Authentication and Authorization")
diff --git a/django/contrib/contenttypes/apps.py b/django/contrib/contenttypes/apps.py
index 1a8e25b98e..390afb3fcf 100644
--- a/django/contrib/contenttypes/apps.py
+++ b/django/contrib/contenttypes/apps.py
@@ -12,6 +12,7 @@ from .management import (
class ContentTypesConfig(AppConfig):
+ default_auto_field = 'django.db.models.AutoField'
name = 'django.contrib.contenttypes'
verbose_name = _("Content Types")
diff --git a/django/contrib/flatpages/apps.py b/django/contrib/flatpages/apps.py
index 330ee05063..4f5ef17004 100644
--- a/django/contrib/flatpages/apps.py
+++ b/django/contrib/flatpages/apps.py
@@ -3,5 +3,6 @@ from django.utils.translation import gettext_lazy as _
class FlatPagesConfig(AppConfig):
+ default_auto_field = 'django.db.models.AutoField'
name = 'django.contrib.flatpages'
verbose_name = _("Flat Pages")
diff --git a/django/contrib/gis/apps.py b/django/contrib/gis/apps.py
index 662ae43d8d..e582e76760 100644
--- a/django/contrib/gis/apps.py
+++ b/django/contrib/gis/apps.py
@@ -4,6 +4,7 @@ from django.utils.translation import gettext_lazy as _
class GISConfig(AppConfig):
+ default_auto_field = 'django.db.models.AutoField'
name = 'django.contrib.gis'
verbose_name = _("GIS")
diff --git a/django/contrib/redirects/apps.py b/django/contrib/redirects/apps.py
index cab67424e7..c1d80ee3c1 100644
--- a/django/contrib/redirects/apps.py
+++ b/django/contrib/redirects/apps.py
@@ -3,5 +3,6 @@ from django.utils.translation import gettext_lazy as _
class RedirectsConfig(AppConfig):
+ default_auto_field = 'django.db.models.AutoField'
name = 'django.contrib.redirects'
verbose_name = _("Redirects")
diff --git a/django/contrib/sitemaps/apps.py b/django/contrib/sitemaps/apps.py
index 502d6890d0..ec795eab87 100644
--- a/django/contrib/sitemaps/apps.py
+++ b/django/contrib/sitemaps/apps.py
@@ -3,5 +3,6 @@ from django.utils.translation import gettext_lazy as _
class SiteMapsConfig(AppConfig):
+ default_auto_field = 'django.db.models.AutoField'
name = 'django.contrib.sitemaps'
verbose_name = _("Site Maps")
diff --git a/django/contrib/sites/apps.py b/django/contrib/sites/apps.py
index ed52d34786..7f820dcc79 100644
--- a/django/contrib/sites/apps.py
+++ b/django/contrib/sites/apps.py
@@ -8,6 +8,7 @@ from .management import create_default_site
class SitesConfig(AppConfig):
+ default_auto_field = 'django.db.models.AutoField'
name = 'django.contrib.sites'
verbose_name = _("Sites")
diff --git a/django/db/models/base.py b/django/db/models/base.py
index de044886c3..822aad080d 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -1290,11 +1290,36 @@ class Model(metaclass=ModelBase):
*cls._check_indexes(databases),
*cls._check_ordering(),
*cls._check_constraints(databases),
+ *cls._check_default_pk(),
]
return errors
@classmethod
+ def _check_default_pk(cls):
+ if (
+ cls._meta.pk.auto_created and
+ not settings.is_overridden('DEFAULT_AUTO_FIELD') and
+ not cls._meta.app_config._is_default_auto_field_overridden
+ ):
+ return [
+ checks.Warning(
+ f"Auto-created primary key used when not defining a "
+ f"primary key type, by default "
+ f"'{settings.DEFAULT_AUTO_FIELD}'.",
+ hint=(
+ f"Configure the DEFAULT_AUTO_FIELD setting or the "
+ f"{cls._meta.app_config.__class__.__qualname__}."
+ f"default_auto_field attribute to point to a subclass "
+ f"of AutoField, e.g. 'django.db.models.BigAutoField'."
+ ),
+ obj=cls,
+ id='models.W042',
+ ),
+ ]
+ return []
+
+ @classmethod
def _check_swappable(cls):
"""Check if the swapped model exists."""
errors = []
diff --git a/django/db/models/options.py b/django/db/models/options.py
index 0e28b6812a..4028e05b99 100644
--- a/django/db/models/options.py
+++ b/django/db/models/options.py
@@ -5,12 +5,13 @@ from collections import defaultdict
from django.apps import apps
from django.conf import settings
-from django.core.exceptions import FieldDoesNotExist
+from django.core.exceptions import FieldDoesNotExist, ImproperlyConfigured
from django.db import connections
from django.db.models import AutoField, Manager, OrderWrt, UniqueConstraint
from django.db.models.query_utils import PathInfo
from django.utils.datastructures import ImmutableList, OrderedSet
from django.utils.functional import cached_property
+from django.utils.module_loading import import_string
from django.utils.text import camel_case_to_spaces, format_lazy
from django.utils.translation import override
@@ -217,6 +218,37 @@ class Options:
new_objs.append(obj)
return new_objs
+ def _get_default_pk_class(self):
+ pk_class_path = getattr(
+ self.app_config,
+ 'default_auto_field',
+ settings.DEFAULT_AUTO_FIELD,
+ )
+ if self.app_config and self.app_config._is_default_auto_field_overridden:
+ app_config_class = type(self.app_config)
+ source = (
+ f'{app_config_class.__module__}.'
+ f'{app_config_class.__qualname__}.default_auto_field'
+ )
+ else:
+ source = 'DEFAULT_AUTO_FIELD'
+ if not pk_class_path:
+ raise ImproperlyConfigured(f'{source} must not be empty.')
+ try:
+ pk_class = import_string(pk_class_path)
+ except ImportError as e:
+ msg = (
+ f"{source} refers to the module '{pk_class_path}' that could "
+ f"not be imported."
+ )
+ raise ImproperlyConfigured(msg) from e
+ if not issubclass(pk_class, AutoField):
+ raise ValueError(
+ f"Primary key '{pk_class_path}' referred by {source} must "
+ f"subclass AutoField."
+ )
+ return pk_class
+
def _prepare(self, model):
if self.order_with_respect_to:
# The app registry will not be ready at this point, so we cannot
@@ -250,7 +282,8 @@ class Options:
field.primary_key = True
self.setup_pk(field)
else:
- auto = AutoField(verbose_name='ID', primary_key=True, auto_created=True)
+ pk_class = self._get_default_pk_class()
+ auto = pk_class(verbose_name='ID', primary_key=True, auto_created=True)
model.add_to_class('id', auto)
def add_manager(self, manager):