Removing `null=True` from `GeneratedField` should be a SQL no-op

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 this 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)

I think this is inconsistent with the message of the warning, and is possibly a bug. I believe removing null=True from GeneratedField should be a SQL no-op.

As you can see here, the DDL for the generated field is the same regardless of whether null=True is passed or not. I think this supports my no-op suggestion.

Thank you. This looks like a pretty complete bug report. We use Trac to manage bugs at https://code.djangoproject.com Do you mind writing this up over there please?

Thanks for the reply @CodenameTim. I opened the ticket #37348 (Removing null=True from GeneratedField should be a SQL no-op) – Django. Turns out this is an actual bug affecting e.g. the results of .exclude() queries.