From e7f464ecdc025000899067ceb6bc61116ca81996 Mon Sep 17 00:00:00 2001 From: Natalia <124304+nessita@users.noreply.github.com> Date: Thu, 17 Sep 2026 09:12:40 -0300 Subject: [PATCH 1/3] Refs #37348 -- Reverted "Refs #36806 -- Removed unnecessary null=True from GeneratedField in test models.". This reverts commit 968f3f96373e028f1486d135e38331fcd0e3a0ca. --- tests/model_fields/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/model_fields/models.py b/tests/model_fields/models.py index 355a58a5b132..cb11be98f32c 100644 --- a/tests/model_fields/models.py +++ b/tests/model_fields/models.py @@ -655,6 +655,7 @@ class GeneratedModelNull(models.Model): expression=Lower("name"), output_field=models.CharField(max_length=10), db_persist=True, + null=True, ) class Meta: @@ -667,6 +668,7 @@ class GeneratedModelNullVirtual(models.Model): expression=Lower("name"), output_field=models.CharField(max_length=10), db_persist=False, + null=True, ) class Meta: From fb32a564ccc6f91d4b7594f5707c62fab0c11c04 Mon Sep 17 00:00:00 2001 From: Natalia <124304+nessita@users.noreply.github.com> Date: Thu, 17 Sep 2026 09:14:05 -0300 Subject: [PATCH 2/3] Fixed #37348 -- Reverted "Fixed #36806 -- Added system check for null kwarg in GeneratedField.". MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 6025eab3c509b4de922117e16866bbfe0ee99aa6, includes a release note for 6.1.2, and keeps fields.W225 documented as removed. Thanks to Michal Porteš for the report, and to Jacob Walls and Simon Charette for reviews. --- django/db/models/fields/generated.py | 16 ------------- docs/ref/checks.txt | 3 ++- docs/ref/models/fields.txt | 3 --- docs/releases/6.1.2.txt | 3 +++ .../test_ordinary_fields.py | 23 ------------------- 5 files changed, 5 insertions(+), 43 deletions(-) diff --git a/django/db/models/fields/generated.py b/django/db/models/fields/generated.py index f90bc560e22f..f89269b5e6e4 100644 --- a/django/db/models/fields/generated.py +++ b/django/db/models/fields/generated.py @@ -30,7 +30,6 @@ def __init__(self, *, expression, output_field, db_persist, **kwargs): self.expression = expression self.output_field = output_field self.db_persist = db_persist - self.has_null_arg = "null" in kwargs super().__init__(**kwargs) @cached_property @@ -83,7 +82,6 @@ def check(self, **kwargs): *super().check(**kwargs), *self._check_supported(databases), *self._check_persistence(databases), - *self._check_ignored_options(databases), ] output_field_clone = self.output_field.clone() output_field_clone.model = self.model @@ -190,20 +188,6 @@ def _check_persistence(self, databases): ) return errors - def _check_ignored_options(self, databases): - warnings = [] - - if self.has_null_arg: - warnings.append( - checks.Warning( - "null has no effect on GeneratedField.", - obj=self, - id="fields.W225", - ) - ) - - return warnings - def deconstruct(self): name, path, args, kwargs = super().deconstruct() del kwargs["blank"] diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt index 14dc374b5763..0704a62315ac 100644 --- a/docs/ref/checks.txt +++ b/docs/ref/checks.txt @@ -234,7 +234,8 @@ Model fields ``GeneratedField``\s. * **fields.E223**: ``GeneratedField.output_field`` has errors: ... * **fields.W224**: ``GeneratedField.output_field`` has warnings: ... -* **fields.W225**: ``null`` has no effect on ``GeneratedField``. +* **fields.W225**: ``null`` has no effect on ``GeneratedField``. *This check + is removed in Django 6.1.2.* * **fields.E900**: ``IPAddressField`` has been removed except for support in historical migrations. * **fields.W900**: ``IPAddressField`` has been deprecated. Support for it diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt index 043b07cb457c..ae958ea5cebb 100644 --- a/docs/ref/models/fields.txt +++ b/docs/ref/models/fields.txt @@ -1366,9 +1366,6 @@ materialized view. .. _PostgreSQL: https://www.postgresql.org/docs/current/ddl-generated-columns.html .. _SQLite: https://www.sqlite.org/gencol.html#limitations -:attr:`~Field.null` has no effect on ``GeneratedField`` since whether the -column is nullable depends on the database and expression used. - ``GenericIPAddressField`` ------------------------- diff --git a/docs/releases/6.1.2.txt b/docs/releases/6.1.2.txt index 810d81d62682..bc5f485c51b9 100644 --- a/docs/releases/6.1.2.txt +++ b/docs/releases/6.1.2.txt @@ -11,3 +11,6 @@ Bugfixes * Fixed a data loss issue in Django 4.0 where :ref:`network rasters ` were deleted by GeoDjango when closed. + +* Fixed a bug in Django 6.1 where the ``fields.W225`` system check incorrectly + warned that ``null`` has no effect on ``GeneratedField`` (:ticket:`37348`). diff --git a/tests/invalid_models_tests/test_ordinary_fields.py b/tests/invalid_models_tests/test_ordinary_fields.py index f445ec83b870..dec8acd16ce6 100644 --- a/tests/invalid_models_tests/test_ordinary_fields.py +++ b/tests/invalid_models_tests/test_ordinary_fields.py @@ -1510,26 +1510,3 @@ class Model(models.Model): Model._meta.get_field("field").check(databases={"default"}), expected_warnings, ) - - @skipUnlessDBFeature("supports_stored_generated_columns") - def test_with_null_argument(self): - class Model(models.Model): - value = models.IntegerField() - field = models.GeneratedField( - expression=models.F("value") * 2, - output_field=models.IntegerField(), - db_persist=True, - null=True, - ) - - expected_warnings = [ - DjangoWarning( - "null has no effect on GeneratedField.", - obj=Model._meta.get_field("field"), - id="fields.W225", - ), - ] - self.assertEqual( - Model._meta.get_field("field").check(databases={"default"}), - expected_warnings, - ) From 2abf9d2cf8602f0ddc0db4ec1a33769b41b4232a Mon Sep 17 00:00:00 2001 From: Natalia <124304+nessita@users.noreply.github.com> Date: Wed, 16 Sep 2026 13:33:56 -0300 Subject: [PATCH 3/3] Refs #37348 -- Added test for excluding on a nullable GeneratedField. Negated lookups rely on `Field.null` to match rows where the value is `NULL`. This behavior was lost when `null=True` was removed from `GeneratedField` definitions to silence `fields.W225`. --- tests/model_fields/test_generatedfield.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/model_fields/test_generatedfield.py b/tests/model_fields/test_generatedfield.py index 161084ecefc3..91bcad86f68b 100644 --- a/tests/model_fields/test_generatedfield.py +++ b/tests/model_fields/test_generatedfield.py @@ -381,6 +381,15 @@ def test_nullable(self): with self.assertNumQueries(expected_num_queries): self.assertEqual(m2.lower_name, "name") + def test_nullable_exclude(self): + m1 = self.nullable_model.objects.create() + m2 = self.nullable_model.objects.create(name="NaMe") + self.nullable_model.objects.create(name="Other") + self.assertSequenceEqual( + self.nullable_model.objects.exclude(lower_name="other").order_by("pk"), + [m1, m2], + ) + @skipUnlessDBFeature("supports_stored_generated_columns") class StoredGeneratedFieldTests(GeneratedFieldTestMixin, TestCase):