﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
34842	Unmanaged read-only generated fields in admin	Paolo Melchiorre	Paolo Melchiorre	"Using read-only generated fields in the admin breaks the add template instance page.

**Model**
{{{
from from django.db import models

class Square(models.Model):
    side = models.IntegerField()
    area = models.GeneratedField(expression=F(""side"") * F(""side""), db_persist=True)
}}}

**Admin**
{{{
from django.contrib import admin
from .models import Square

@admin.register(Square)
class SquareAdmin(admin.ModelAdmin):
    readonly_fields = (""area"",)
}}}

**Steps**
1) Open the creation page (es: http://localhost:8000/admin/geometricfigures/square/add/)

**Traceback**

{{{
Environment:


Request Method: GET
Request URL: http://localhost:8000/admin/geometricfigures/square/add/

Django Version: 5.0.dev20230915033643
Python Version: 3.11.4
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'django.contrib.postgres',
 'django.contrib.gis',
 'geometricfigures']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']


Template error:
In template /home/paulox/Projects/django/django/contrib/admin/templates/admin/includes/fieldset.html, error at line 18
   Cannot read a generated field from an unsaved model.
   8 :             {% if line.fields|length == 1 %}{{ line.errors }}{% else %}<div class=""flex-container form-multiline"">{% endif %}
   9 :             {% for field in line %}
   10 :                 <div>
   11 :                     {% if not line.fields|length == 1 and not field.is_readonly %}{{ field.errors }}{% endif %}
   12 :                         <div class=""flex-container{% if not line.fields|length == 1 %} fieldBox{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% if not field.is_readonly and field.errors %} errors{% endif %}{% if field.field.is_hidden %} hidden{% endif %}{% elif field.is_checkbox %} checkbox-row{% endif %}"">
   13 :                             {% if field.is_checkbox %}
   14 :                                 {{ field.field }}{{ field.label_tag }}
   15 :                             {% else %}
   16 :                                 {{ field.label_tag }}
   17 :                                 {% if field.is_readonly %}
   18 :                                     <div class=""readonly""> {{ field.contents }} </div>
   19 :                                 {% else %}
   20 :                                     {{ field.field }}
   21 :                                 {% endif %}
   22 :                             {% endif %}
   23 :                         </div>
   24 :                     {% if field.field.help_text %}
   25 :                         <div class=""help""{% if field.field.id_for_label %} id=""{{ field.field.id_for_label }}_helptext""{% endif %}>
   26 :                             <div>{{ field.field.help_text|safe }}</div>
   27 :                         </div>
   28 :                     {% endif %}


Traceback (most recent call last):
  File ""/home/paulox/Projects/django/django/core/handlers/exception.py"", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/core/handlers/base.py"", line 220, in _get_response
    response = response.render()
               ^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/response.py"", line 114, in render
    self.content = self.rendered_content
                   ^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/response.py"", line 92, in rendered_content
    return template.render(context, self._request)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/backends/django.py"", line 61, in render
    return self.template.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 171, in render
    return self._render(context)
           ^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 163, in _render
    return self.nodelist.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 1000, in render
    return SafeString("""".join([node.render_annotated(context) for node in self]))
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 1000, in <listcomp>
    return SafeString("""".join([node.render_annotated(context) for node in self]))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 961, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/loader_tags.py"", line 159, in render
    return compiled_parent._render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 163, in _render
    return self.nodelist.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 1000, in render
    return SafeString("""".join([node.render_annotated(context) for node in self]))
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 1000, in <listcomp>
    return SafeString("""".join([node.render_annotated(context) for node in self]))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 961, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/loader_tags.py"", line 159, in render
    return compiled_parent._render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 163, in _render
    return self.nodelist.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 1000, in render
    return SafeString("""".join([node.render_annotated(context) for node in self]))
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 1000, in <listcomp>
    return SafeString("""".join([node.render_annotated(context) for node in self]))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 961, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/loader_tags.py"", line 65, in render
    result = block.nodelist.render(context)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 1000, in render
    return SafeString("""".join([node.render_annotated(context) for node in self]))
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 1000, in <listcomp>
    return SafeString("""".join([node.render_annotated(context) for node in self]))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 961, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/loader_tags.py"", line 65, in render
    result = block.nodelist.render(context)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 1000, in render
    return SafeString("""".join([node.render_annotated(context) for node in self]))
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 1000, in <listcomp>
    return SafeString("""".join([node.render_annotated(context) for node in self]))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 961, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/defaulttags.py"", line 241, in render
    nodelist.append(node.render_annotated(context))
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 961, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/loader_tags.py"", line 210, in render
    return template.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 173, in render
    return self._render(context)
           ^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 163, in _render
    return self.nodelist.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 1000, in render
    return SafeString("""".join([node.render_annotated(context) for node in self]))
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 1000, in <listcomp>
    return SafeString("""".join([node.render_annotated(context) for node in self]))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 961, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/defaulttags.py"", line 241, in render
    nodelist.append(node.render_annotated(context))
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 961, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/defaulttags.py"", line 241, in render
    nodelist.append(node.render_annotated(context))
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 961, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/defaulttags.py"", line 325, in render
    return nodelist.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 1000, in render
    return SafeString("""".join([node.render_annotated(context) for node in self]))
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 1000, in <listcomp>
    return SafeString("""".join([node.render_annotated(context) for node in self]))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 961, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/defaulttags.py"", line 325, in render
    return nodelist.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 1000, in render
    return SafeString("""".join([node.render_annotated(context) for node in self]))
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 1000, in <listcomp>
    return SafeString("""".join([node.render_annotated(context) for node in self]))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 961, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 1059, in render
    output = self.filter_expression.resolve(context)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 710, in resolve
    obj = self.var.resolve(context)
          ^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 842, in resolve
    value = self._resolve_lookup(context)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/template/base.py"", line 909, in _resolve_lookup
    current = current()
              ^^^^^^^^^
  File ""/home/paulox/Projects/django/django/contrib/admin/helpers.py"", line 271, in contents
    f, attr, value = lookup_field(field, obj, model_admin)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/contrib/admin/utils.py"", line 308, in lookup_field
    value = getattr(obj, name)
            ^^^^^^^^^^^^^^^^^^
  File ""/home/paulox/Projects/django/django/db/models/query_utils.py"", line 202, in __get__
    raise FieldError(
    ^

Exception Type: FieldError at /admin/geometricfigures/square/add/
Exception Value: Cannot read a generated field from an unsaved model.
}}}

**Patch**


{{{
diff --git a/django/contrib/admin/helpers.py b/django/contrib/admin/helpers.py
index 90ca7affc8..f7e45b408c 100644
--- a/django/contrib/admin/helpers.py
+++ b/django/contrib/admin/helpers.py
@@ -9,7 +9,7 @@ from django.contrib.admin.utils import (
     lookup_field,
     quote,
 )
-from django.core.exceptions import ObjectDoesNotExist
+from django.core.exceptions import FieldError, ObjectDoesNotExist
 from django.db.models.fields.related import (
     ForeignObjectRel,
     ManyToManyRel,
@@ -268,7 +268,7 @@ class AdminReadonlyField:
         )
         try:
             f, attr, value = lookup_field(field, obj, model_admin)
-        except (AttributeError, ValueError, ObjectDoesNotExist):
+        except (AttributeError, ValueError, ObjectDoesNotExist, FieldError):
             result_repr = self.empty_value_display
         else:
             if field in self.form.fields:
}}}

"	Bug	closed	Database layer (models, ORM)	dev	Release blocker	fixed	field, database, generated, admin		Ready for checkin	1	0	0	0	0	0
