summaryrefslogtreecommitdiff
path: root/django/db/models/fields
diff options
context:
space:
mode:
authorIan Foote <python@ian.feete.org>2020-11-22 22:27:57 +0000
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2023-05-12 19:11:40 +0200
commit7414704e88d73dafbcfbb85f9bc54cb6111439d3 (patch)
treef51136b16e457d7f46e01ff3cc06308faf0923db /django/db/models/fields
parent599f3e2cda50ab084915ffd08edb5ad6cad61415 (diff)
Fixed #470 -- Added support for database defaults on fields.
Special thanks to Hannes Ljungberg for finding multiple implementation gaps. Thanks also to Simon Charette, Adam Johnson, and Mariusz Felisiak for reviews.
Diffstat (limited to 'django/db/models/fields')
-rw-r--r--django/db/models/fields/__init__.py61
1 files changed, 59 insertions, 2 deletions
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index 4416898d80..18b48c0e72 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -202,6 +202,7 @@ class Field(RegisterLookupMixin):
validators=(),
error_messages=None,
db_comment=None,
+ db_default=NOT_PROVIDED,
):
self.name = name
self.verbose_name = verbose_name # May be set by set_attributes_from_name
@@ -212,6 +213,13 @@ class Field(RegisterLookupMixin):
self.remote_field = rel
self.is_relation = self.remote_field is not None
self.default = default
+ if db_default is not NOT_PROVIDED and not hasattr(
+ db_default, "resolve_expression"
+ ):
+ from django.db.models.expressions import Value
+
+ db_default = Value(db_default)
+ self.db_default = db_default
self.editable = editable
self.serialize = serialize
self.unique_for_date = unique_for_date
@@ -263,6 +271,7 @@ class Field(RegisterLookupMixin):
return [
*self._check_field_name(),
*self._check_choices(),
+ *self._check_db_default(**kwargs),
*self._check_db_index(),
*self._check_db_comment(**kwargs),
*self._check_null_allowed_for_primary_keys(),
@@ -379,6 +388,39 @@ class Field(RegisterLookupMixin):
)
]
+ def _check_db_default(self, databases=None, **kwargs):
+ from django.db.models.expressions import Value
+
+ if (
+ self.db_default is NOT_PROVIDED
+ or isinstance(self.db_default, Value)
+ or databases is None
+ ):
+ return []
+ errors = []
+ for db in databases:
+ if not router.allow_migrate_model(db, self.model):
+ continue
+ connection = connections[db]
+
+ if not getattr(self.db_default, "allowed_default", False) and (
+ connection.features.supports_expression_defaults
+ ):
+ msg = f"{self.db_default} cannot be used in db_default."
+ errors.append(checks.Error(msg, obj=self, id="fields.E012"))
+
+ if not (
+ connection.features.supports_expression_defaults
+ or "supports_expression_defaults"
+ in self.model._meta.required_db_features
+ ):
+ msg = (
+ f"{connection.display_name} does not support default database "
+ "values with expressions (db_default)."
+ )
+ errors.append(checks.Error(msg, obj=self, id="fields.E011"))
+ return errors
+
def _check_db_index(self):
if self.db_index not in (None, True, False):
return [
@@ -558,6 +600,7 @@ class Field(RegisterLookupMixin):
"null": False,
"db_index": False,
"default": NOT_PROVIDED,
+ "db_default": NOT_PROVIDED,
"editable": True,
"serialize": True,
"unique_for_date": None,
@@ -876,7 +919,10 @@ class Field(RegisterLookupMixin):
@property
def db_returning(self):
"""Private API intended only to be used by Django itself."""
- return False
+ return (
+ self.db_default is not NOT_PROVIDED
+ and connection.features.can_return_columns_from_insert
+ )
def set_attributes_from_name(self, name):
self.name = self.name or name
@@ -929,7 +975,13 @@ class Field(RegisterLookupMixin):
def pre_save(self, model_instance, add):
"""Return field's value just before saving."""
- return getattr(model_instance, self.attname)
+ value = getattr(model_instance, self.attname)
+ if not connection.features.supports_default_keyword_in_insert:
+ from django.db.models.expressions import DatabaseDefault
+
+ if isinstance(value, DatabaseDefault):
+ return self.db_default
+ return value
def get_prep_value(self, value):
"""Perform preliminary non-db specific value checks and conversions."""
@@ -968,6 +1020,11 @@ class Field(RegisterLookupMixin):
return self.default
return lambda: self.default
+ if self.db_default is not NOT_PROVIDED:
+ from django.db.models.expressions import DatabaseDefault
+
+ return DatabaseDefault
+
if (
not self.empty_strings_allowed
or self.null