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

  • sqlmigrate of 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 migrate fails 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.

Change History (0)

Note: See TracTickets for help on using tickets.
Back to Top