From 7414704e88d73dafbcfbb85f9bc54cb6111439d3 Mon Sep 17 00:00:00 2001 From: Ian Foote Date: Sun, 22 Nov 2020 22:27:57 +0000 Subject: 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. --- docs/ref/checks.txt | 3 +++ docs/ref/models/expressions.txt | 7 +++++++ docs/ref/models/fields.txt | 35 +++++++++++++++++++++++++++++++++++ docs/ref/models/instances.txt | 14 ++++++++++---- docs/releases/5.0.txt | 26 +++++++++++++++++++++++++- 5 files changed, 80 insertions(+), 5 deletions(-) (limited to 'docs') diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt index 9e350a3ff3..df0adbef63 100644 --- a/docs/ref/checks.txt +++ b/docs/ref/checks.txt @@ -175,6 +175,9 @@ Model fields ``choices`` (```` characters). * **fields.E010**: ```` default should be a callable instead of an instance so that it's not shared between all field instances. +* **fields.E011**: ```` does not support default database values with + expressions (``db_default``). +* **fields.E012**: ```` cannot be used in ``db_default``. * **fields.E100**: ``AutoField``\s must set primary_key=True. * **fields.E110**: ``BooleanField``\s do not accept null values. *This check appeared before support for null values was added in Django 2.1.* diff --git a/docs/ref/models/expressions.txt b/docs/ref/models/expressions.txt index 52a1022771..50560dfa9b 100644 --- a/docs/ref/models/expressions.txt +++ b/docs/ref/models/expressions.txt @@ -996,6 +996,13 @@ calling the appropriate methods on the wrapped expression. .. class:: Expression + .. attribute:: allowed_default + + .. versionadded:: 5.0 + + Tells Django that this expression can be used in + :attr:`Field.db_default`. Defaults to ``False``. + .. attribute:: contains_aggregate Tells Django that this expression contains an aggregate and that a diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt index 27b87c1f53..344cc45280 100644 --- a/docs/ref/models/fields.txt +++ b/docs/ref/models/fields.txt @@ -351,6 +351,38 @@ looking at your Django code. For example:: db_comment="Date and time when the article was published", ) +``db_default`` +-------------- + +.. versionadded:: 5.0 + +.. attribute:: Field.db_default + +The database-computed default value for this field. This can be a literal value +or a database function, such as :class:`~django.db.models.functions.Now`:: + + created = models.DateTimeField(db_default=Now()) + +More complex expressions can be used, as long as they are made from literals +and database functions:: + + month_due = models.DateField( + db_default=TruncMonth( + Now() + timedelta(days=90), + output_field=models.DateField(), + ) + ) + +Database defaults cannot reference other fields or models. For example, this is +invalid:: + + end = models.IntegerField(db_default=F("start") + 50) + +If both ``db_default`` and :attr:`Field.default` are set, ``default`` will take +precedence when creating instances in Python code. ``db_default`` will still be +set at the database level and will be used when inserting rows outside of the +ORM or when adding a new field in a migration. + ``db_index`` ------------ @@ -408,6 +440,9 @@ The default value is used when new model instances are created and a value isn't provided for the field. When the field is a primary key, the default is also used when the field is set to ``None``. +The default value can also be set at the database level with +:attr:`Field.db_default`. + ``editable`` ------------ diff --git a/docs/ref/models/instances.txt b/docs/ref/models/instances.txt index d03b05577c..346ae55130 100644 --- a/docs/ref/models/instances.txt +++ b/docs/ref/models/instances.txt @@ -541,7 +541,8 @@ You may have noticed Django database objects use the same ``save()`` method for creating and changing objects. Django abstracts the need to use ``INSERT`` or ``UPDATE`` SQL statements. Specifically, when you call ``save()`` and the object's primary key attribute does **not** define a -:attr:`~django.db.models.Field.default`, Django follows this algorithm: +:attr:`~django.db.models.Field.default` or +:attr:`~django.db.models.Field.db_default`, Django follows this algorithm: * If the object's primary key attribute is set to a value that evaluates to ``True`` (i.e., a value other than ``None`` or the empty string), Django @@ -551,9 +552,10 @@ object's primary key attribute does **not** define a exist in the database), Django executes an ``INSERT``. If the object's primary key attribute defines a -:attr:`~django.db.models.Field.default` then Django executes an ``UPDATE`` if -it is an existing model instance and primary key is set to a value that exists -in the database. Otherwise, Django executes an ``INSERT``. +:attr:`~django.db.models.Field.default` or +:attr:`~django.db.models.Field.db_default` then Django executes an ``UPDATE`` +if it is an existing model instance and primary key is set to a value that +exists in the database. Otherwise, Django executes an ``INSERT``. The one gotcha here is that you should be careful not to specify a primary-key value explicitly when saving new objects, if you cannot guarantee the @@ -570,6 +572,10 @@ which returns ``NULL``. In such cases it is possible to revert to the old algorithm by setting the :attr:`~django.db.models.Options.select_on_save` option to ``True``. +.. versionchanged:: 5.0 + + The ``Field.db_default`` parameter was added. + .. _ref-models-force-insert: Forcing an INSERT or UPDATE diff --git a/docs/releases/5.0.txt b/docs/releases/5.0.txt index f23f39b014..d40cd6a4f0 100644 --- a/docs/releases/5.0.txt +++ b/docs/releases/5.0.txt @@ -108,6 +108,21 @@ Can now be simplified to: per-project, per-field, or per-request basis. See :ref:`reusable-field-group-templates`. +Database-computed default values +-------------------------------- + +The new :attr:`Field.db_default ` parameter +sets a database-computed default value. For example:: + + from django.db import models + from django.db.models.functions import Now, Pi + + + class MyModel(models.Model): + age = models.IntegerField(db_default=18) + created = models.DateTimeField(db_default=Now()) + circumference = models.FloatField(db_default=2 * Pi()) + Minor features -------------- @@ -355,7 +370,16 @@ Database backend API This section describes changes that may be needed in third-party database backends. -* ... +* ``DatabaseFeatures.supports_expression_defaults`` should be set to ``False`` + if the database doesn't support using database functions as defaults. + +* ``DatabaseFeatures.supports_default_keyword_in_insert`` should be set to + ``False`` if the database doesn't support the ``DEFAULT`` keyword in + ``INSERT`` queries. + +* ``DatabaseFeatures.supports_default_keyword_in_bulk insert`` should be set to + ``False`` if the database doesn't support the ``DEFAULT`` keyword in bulk + ``INSERT`` queries. Using ``create_defaults__exact`` may now be required with ``QuerySet.update_or_create()`` ----------------------------------------------------------------------------------------- -- cgit v1.2.1