Opened 59 minutes ago
#37348 new Bug
Removing `null=True` from GeneratedField should be a SQL no-op
| Reported by: | Michal Porteš | Owned by: | |
|---|---|---|---|
| Component: | Database layer (models, ORM) | Version: | 6.1 |
| Severity: | Normal | Keywords: | |
| Cc: | Triage Stage: | Unreviewed | |
| Has patch: | no | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
As of Django 6.1, having a GeneratedField declared with an explicit null=True causes the warning (fields.W225) null has no effect on GeneratedField.
Consider the following minimal example:
class NumericPrefix(models.Func):
function = "substring"
template = r"%(function)s(%(expressions)s from '^\d+')"
class Foo(models.Model):
code = models.CharField(max_length=50)
code_num_prefix = models.GeneratedField(
expression=NumericPrefix("code"),
output_field=models.CharField(max_length=50),
db_persist=True,
null=True,
)
When I try to resolve the warning by removing null=True, the following happens:
- the
makemigrationscommand prompts the dialog
It is impossible to change a nullable field 'code_num_prefix' on foo to non-nullable without providing a default. This is because the database needs something to populate existing rows. Please select a fix: 1) Provide a one-off default now (will be set on all existing rows with a null value for this column) 2) Ignore for now. Existing rows that contain NULL values will have to be handled manually, for example with a RunPython or RunSQL operation. 3) Quit and manually define a default value in models.py.
I select 2 because my table contains NULL values that I don’t want to change.
sqlmigrateof the new migration outputs the following SQL:
BEGIN; -- -- Alter field code_num_prefix on foo -- ALTER TABLE "foo_foo" ALTER COLUMN "code_num_prefix" SET NOT NULL; COMMIT;
- attempting to run
migratefails with
django.db.utils.IntegrityError: column "code_num_prefix" of relation "foo_foo" contains null values
(as mentioned above — my table contains NULL values)
These are all familiar steps when altering a normal column from nullable to non-nullable. But in this case it's unexpected and inconsistent with the warning's message.
This example can be used to demonstrate that the DDL for the generated field is the same regardless of whether null=True is passed or not. Therefore I believe that this might be a bug and that removing null=True from GeneratedField should be a no-op at the database level.