diff --git a/django/db/models/base.py b/django/db/models/base.py index b15bdd032ab0..61925f63ea30 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -1628,7 +1628,7 @@ def clean_fields(self, exclude=None): errors = {} for f in self._meta.fields: - if f.name in exclude: + if f.name in exclude or f.generated: continue # Skip validation for empty fields with blank=True. The developer # is responsible for making sure they have a valid value. diff --git a/docs/releases/5.0.2.txt b/docs/releases/5.0.2.txt index a5e2b6eee407..05f80bb00f92 100644 --- a/docs/releases/5.0.2.txt +++ b/docs/releases/5.0.2.txt @@ -15,3 +15,6 @@ Bugfixes * Fixed a regression in Django 5.0 where links in the admin had an incorrect color (:ticket:`35121`). + +* Fixed a bug in Django 5.0 that caused a crash of ``Model.full_clean()`` on + models with a ``GeneratedField`` (:ticket:`35127`). diff --git a/tests/model_fields/models.py b/tests/model_fields/models.py index 69b4e26145ce..e34f3c8947aa 100644 --- a/tests/model_fields/models.py +++ b/tests/model_fields/models.py @@ -502,7 +502,7 @@ class GeneratedModel(models.Model): output_field=models.IntegerField(), db_persist=True, ) - fk = models.ForeignKey(Foo, on_delete=models.CASCADE, null=True) + fk = models.ForeignKey(Foo, on_delete=models.CASCADE, null=True, blank=True) class Meta: required_db_features = {"supports_stored_generated_columns"} @@ -516,7 +516,7 @@ class GeneratedModelVirtual(models.Model): output_field=models.IntegerField(), db_persist=False, ) - fk = models.ForeignKey(Foo, on_delete=models.CASCADE, null=True) + fk = models.ForeignKey(Foo, on_delete=models.CASCADE, null=True, blank=True) class Meta: required_db_features = {"supports_virtual_generated_columns"} diff --git a/tests/model_fields/test_generatedfield.py b/tests/model_fields/test_generatedfield.py index 589f78cbb042..a636e984fdbe 100644 --- a/tests/model_fields/test_generatedfield.py +++ b/tests/model_fields/test_generatedfield.py @@ -168,6 +168,14 @@ def test_unsaved_error(self): with self.assertRaisesMessage(AttributeError, msg): m.field + def test_full_clean(self): + m = self.base_model(a=1, b=2) + # full_clean() ignores GeneratedFields. + m.full_clean() + m.save() + m = self._refresh_if_needed(m) + self.assertEqual(m.field, 3) + def test_create(self): m = self.base_model.objects.create(a=1, b=2) m = self._refresh_if_needed(m)