From 2f1ab16be54255213d5c3e4d925a3a24997dc917 Mon Sep 17 00:00:00 2001 From: Paolo Melchiorre Date: Fri, 15 Sep 2023 15:30:06 +0200 Subject: [PATCH] Fixed #34842 -- Fixed ModelAdmin.readonly_fields crash with GeneratedFields. --- django/db/models/query_utils.py | 2 +- tests/admin_views/admin.py | 6 ++++++ tests/admin_views/models.py | 11 +++++++++++ tests/admin_views/tests.py | 5 +++++ tests/model_fields/test_generatedfield.py | 3 +-- 5 files changed, 24 insertions(+), 3 deletions(-) diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py index 9754864eef5..4f3358eb8da 100644 --- a/django/db/models/query_utils.py +++ b/django/db/models/query_utils.py @@ -199,7 +199,7 @@ class DeferredAttribute: val = self._check_parent_chain(instance) if val is None: if instance.pk is None and self.field.generated: - raise FieldError( + raise AttributeError( "Cannot read a generated field from an unsaved model." ) instance.refresh_from_db(fields=[field_name]) diff --git a/tests/admin_views/admin.py b/tests/admin_views/admin.py index aa8b183f146..389e1e8370e 100644 --- a/tests/admin_views/admin.py +++ b/tests/admin_views/admin.py @@ -119,6 +119,7 @@ from .models import ( Simple, Sketch, Song, + Square, State, Story, StumpJoke, @@ -1175,6 +1176,10 @@ class TravelerAdmin(admin.ModelAdmin): autocomplete_fields = ["living_country"] +class SquareAdmin(admin.ModelAdmin): + readonly_fields = ("area",) + + site = admin.AdminSite(name="admin") site.site_url = "/my-site-url/" site.register(Article, ArticleAdmin) @@ -1298,6 +1303,7 @@ site.register(UserProxy) site.register(Box) site.register(Country, CountryAdmin) site.register(Traveler, TravelerAdmin) +site.register(Square, SquareAdmin) # Register core models we need in our tests site.register(User, UserAdmin) diff --git a/tests/admin_views/models.py b/tests/admin_views/models.py index 85facf88214..de16796e85a 100644 --- a/tests/admin_views/models.py +++ b/tests/admin_views/models.py @@ -1134,3 +1134,14 @@ class Traveler(models.Model): related_name="favorite_country_to_vacation_set", limit_choices_to={"continent": Country.ASIA}, ) + + +class Square(models.Model): + side = models.IntegerField() + area = models.GeneratedField( + db_persist=True, + expression=models.F("side") * models.F("side"), + ) + + class Meta: + required_db_features = {"supports_stored_generated_columns"} diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py index e7b2519eefd..164de1d01cb 100644 --- a/tests/admin_views/tests.py +++ b/tests/admin_views/tests.py @@ -6861,6 +6861,11 @@ class ReadonlyTest(AdminFieldExtractionMixin, TestCase): field = self.get_admin_readonly_field(response, "plotdetails") self.assertEqual(field.contents(), "-") # default empty value + @skipUnlessDBFeature("supports_stored_generated_columns") + def test_readonly_unsaved_generated_field(self): + response = self.client.get(reverse("admin:admin_views_square_add")) + self.assertContains(response, '
-
') + @ignore_warnings(category=RemovedInDjango60Warning) def test_readonly_field_overrides(self): """ diff --git a/tests/model_fields/test_generatedfield.py b/tests/model_fields/test_generatedfield.py index dec1f3a31fd..0d81cb82818 100644 --- a/tests/model_fields/test_generatedfield.py +++ b/tests/model_fields/test_generatedfield.py @@ -1,4 +1,3 @@ -from django.core.exceptions import FieldError from django.db import IntegrityError, connection from django.db.models import F, FloatField, GeneratedField, IntegerField, Model from django.db.models.functions import Lower @@ -93,7 +92,7 @@ class GeneratedFieldTestMixin: def test_unsaved_error(self): m = self.base_model(a=1, b=2) msg = "Cannot read a generated field from an unsaved model." - with self.assertRaisesMessage(FieldError, msg): + with self.assertRaisesMessage(AttributeError, msg): m.field def test_create(self):