#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.