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, - ) 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: 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):