#470: Add Field.db_default for defining database defaults
-------------------------------------+-------------------------------------
     Reporter:  jws                  |                    Owner:  John
                                     |  Whitlock
         Type:  New feature          |                   Status:  assigned
    Component:  Database layer       |                  Version:
  (models, ORM)                      |
     Severity:  normal               |               Resolution:
     Keywords:  sql schema           |             Triage Stage:  Accepted
    Has patch:  1                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  1
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------

Comment (by John Whitlock):

 I've reviewed the tests (so many tests!), the database code, and PR 13709.
 While PR 13709 worked, I'm going to try a different approach. As Marcin
 Nowak noted in May 2020, the SQL for adding a column looks like:

 {{{
 ALTER TABLE "x" ADD COLUMN "y" integer DEFAULT 0 NOT NULL CHECK ("y" >=
 0);
 ALTER TABLE "x" ALTER COLUMN "y" DROP DEFAULT;
 }}}

 That's the simple case. SQLite is a bit more complex, because it doesn't
 support the `ALTER COLUMN ... DROP DEFAULT` clause, and re-creates the
 table to get the same effect. A `DateTimeField` with `auto_add_now` looks
 like this on PostgreSQL:

 {{{
 BEGIN;
 ALTER TABLE "x" ADD COLUMN "y" timestamp with time zone DEFAULT
 '2022-09-06T14:37:26.493371+00:00'::timestamptz NOT NULL;
 ALTER TABLE "x" ALTER COLUMN "y" DROP DEFAULT;
 COMMIT;
 }}}

 The current behaviour is:

 1. Add the column with a `DEFAULT` of a static value of the field
 `default` (for example, computing the current time), to populate existing
 row
 2. Drop the column `DEFAULT`

 I want to keep this as the default behaviour, but allow a new behaviour
 for step 2:

 1. Add the column with a `DEFAULT` of a static value of the field
 `default` (for example, computing the current time), to populate existing
 row
 2. Update the column `DEFAULT` to the value of the field `db_default`
 value or expression. If it has the same value as step 1, do nothing

 For the integer case, the SQL would just be the the first line

 {{{
 ALTER TABLE "x" ADD COLUMN "y" integer DEFAULT 0 NOT NULL CHECK ("y" >=
 0);
 }}}

 For the datetime case, the SQL might be:

 {{{
 BEGIN;
 ALTER TABLE "x" ADD COLUMN "y" timestamp with time zone DEFAULT
 '2022-09-06T14:37:26.493371+00:00'::timestamptz NOT NULL;
 ALTER TABLE "x" ALTER COLUMN "y" DEFAULT CURRENT_TIMESTAMP AT TIME ZONE
 'utc';
 COMMIT;
 }}}

 Some other features:
 * `db_default` takes a new constant `NO_DEFAULT` for the current behaviour
 of dropping the `DEFAULT` clause
 * `db_default` takes a new constant `USE_DEFAULT` to copy the value of
 `default`
 * `db_default` allows static values or a class derived from a new
 `DefaultExpression` class, a subclass of `Expression`
 * `db_default` does not allow a generic callable, unlike `default`
 * Classes like `DateTimeField` have logic to "do the right thing" when
 `db_default=USE_DEFAULT` is combined with `auto_add_now` or `auto_now`.
 * Classes like `AutoField` may have more restrictive rules for
 `db_default`, since they maintain control of how the database chooses new
 values.

 I think there should be a way to set a general preference for `NO_DEFAULT`
 or `USE_DEFAULT`, at the Model Options, AppConfig, and Settings level,
 similar to how `DEFAULT_AUTO_FIELD` was implemented in Django 3.2

 A few use cases:
 * Developers that prefer the current behavior do nothing.
 * Developers that use simple `default` values and want similar database
 defaults use `USE_DEFAULT` at the Settings level. A migration is needed to
 add database defaults across the application. They can add overrides at
 the Field, Model Options, and AppConfig level to fix issues or avoid the
 new behavior.
 * Developers that want a database-default for an individual field, to
 support a rolling deployment, can set `db_default=USE_DEFAULT` for added
 columns, and remove it (if desired) in a future migration and deployment.

 I'm open to feedback, here or on the django-developers group.

-- 
Ticket URL: <https://code.djangoproject.com/ticket/470#comment:52>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/010701831357c572-4404439b-ccdc-4eac-94f9-dd9d0e904d42-000000%40eu-central-1.amazonses.com.

Reply via email to