Opened 6 years ago
Last modified 3 days ago
#30577 new New feature
Custom rendering for readonly fields in admin
| Reported by: | David | Owned by: | nobody |
|---|---|---|---|
| Component: | contrib.admin | Version: | 5.1 |
| Severity: | Normal | Keywords: | |
| Cc: | Florian Demmer, Carlos Palol, ldeluigi, Mariusz Felisiak | Triage Stage: | Accepted |
| Has patch: | no | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
The new view permission is extremely useful, and encourages more use of the Django Admin tool. It has highlighted a limitation in the rendering of readonly_fields that can be easily addressed. At the moment, readonly_fields (or all fields when the user has only can_view) can only have custom rendering or formatting if they are custom properties or new fields (created in the ModelAdmin), existing fields can't be changed. In my use-case I have a number of rich text enhanced TextFields, which when rendered as read-only show up as HTML and can't be marked safe. In this case creating a custom field in the ModelAdmin where the Field can be output with mark_safe() doesn't work as the original field needs to exist for users with change permissions, the only other way is to include the field twice which creates UX issues.
If, in ModelAdmin, you could override the formatting/output of a read-only field it would address this issue. Alternatively, as mentioned in the comments of #14802, the idea of Fields having a method that is called by admin to handle the read-only HTML rendering would also do the trick as is related.
Change History (16)
comment:2 by , 6 years ago
| Resolution: | → needsinfo |
|---|---|
| Status: | new → closed |
comment:3 by , 4 years ago
| Cc: | added |
|---|
comment:4 by , 4 years ago
Just ended up here while looking for a way to have a custom widget on a read-only JSONField and bumping into ReadOnlyPasswordHashWidget hackery on my own...
Wouldn't it be cleaner (and easy) to simple update ​https://github.com/django/django/blob/main/django/contrib/admin/templates/admin/includes/fieldset.html#L16-L18 to check for an existing field.readonly_widget all the time?
As I'm not sure how to use the readonlyhashwidget step (field needs to *not* be readonly and widget then is readonly...?), I've gone the route of simply using custom methods to render the "field" but it's not as reusable as being able to set globally used readonly-widgets per form.Field class...
comment:5 by , 4 years ago
| Cc: | added |
|---|
follow-ups: 7 9 comment:6 by , 17 months ago
| Cc: | added |
|---|---|
| Resolution: | needsinfo |
| Status: | closed → new |
| Version: | 2.2 → 5.1 |
I'd like to reopen this ticket in favor of another use case scenario:
I have plenty of models that each have many DateTimeFields that are read only, namely 'updated' and 'created' timestamps.
I want to customize how datetimes are displayed as HTML when they are read_only, but unfortunately Django admin invokes the hardcoded display functions to display read_only fields that don't have a widget set with read_only=True. I don't want to customize it once for each of these fields, I'd like to make a common abstract class that inherits from ModelAdmin once for all my forms and have it change the generated HTML for datetimefields for every subclass.
Using formfield_overrides with a widget with read_only=True isn't enough because the AdminReadonlyField contents method wants the field to be explicitly listed in the form fields before checking if read_only is True on the widget.
I'd like to be able to provide a read only representation of field classes from a ModelAdmin superclass.
comment:7 by , 17 months ago
| Resolution: | → needsinfo |
|---|---|
| Status: | new → closed |
Replying to ldeluigi:
I'd like to reopen this ticket in favor of another use case scenario:
I have plenty of models that each have many DateTimeFields that are read only, namely 'updated' and 'created' timestamps.
I want to customize how datetimes are displayed as HTML when they are read_only, but unfortunately Django admin invokes the hardcoded display functions to display read_only fields that don't have a widget set with read_only=True. I don't want to customize it once for each of these fields, I'd like to make a common abstract class that inherits from ModelAdmin once for all my forms and have it change the generated HTML for datetimefields for every subclass.
Using formfield_overrides with a widget with read_only=True isn't enough because the AdminReadonlyField contents method wants the field to be explicitly listed in the form fields before checking if read_only is True on the widget.
I'd like to be able to provide a read only representation of field classes from a ModelAdmin superclass.
Thanks for reopening this ticket, however you didn't answer any of Carlton's requests/questions or provide PoC. Please don't reopen old ticket without providing required details.
comment:8 by , 17 months ago
| Cc: | added |
|---|
comment:9 by , 17 months ago
| Resolution: | needsinfo |
|---|---|
| Status: | closed → new |
Here is the PoC where I've defined an AbstractAdmin that basically monkeypatches the feature for all subclasses that expose readonly DateTimeField(s) values:
​https://github.com/ldeluigi/django-poc-20240610
The interesting part is in the example app admin.py file: ​https://github.com/ldeluigi/django-poc-20240610/blob/master/example/admin.py
I'd like to be able to do the same without having to write that ugly and cumbersome code which as a side effect has to alter the field names inside the model admin field and fieldsets lists disruptively, breaking the rest of the code not aware of the renaming of those from something to something_local.
In my PoC I can't take advantage of the read_only field on widgets because I can't define an abstract admin form class that works for every subclass, because its subclass define DateTimeFields with different names and not every one of them is read only potentially.
What I need is something that allows me to customize the display of readonly field based on their type that is only used if they are read only for whatever reason, including because of missing permissions.
Example of possible API to solve the problem:
class AbstractAdmin(admin.ModelAdmin): readonly_formfield_overrides = { DateTimeField: {'widget': MyCustomReadonlyWidget}, }
if widgets are not the best choice for readonly representation better work with functions:
def datetime_to_html(datetime: datetime | None) -> SafeText: if datetime is None: return mark_safe('never') return ... class AbstractAdmin(admin.ModelAdmin): readonly_formfield_overrides = { DateTimeField: datetime_to_html, }
Where readonly_formfield_overrides would only be used for non-editable fields whose type one of the specified for customization
comment:10 by , 17 months ago
| Resolution: | → needsinfo |
|---|---|
| Status: | new → closed |
Thank you for providing your insights here Ideluigi, this helps illustrate a potential approach if we want to take this forward.
From reading the ticket through, I believe before we try to develop a feature, we want to see a sample project/example of this issue in action. This will need to be clear what we see and what we want to see instead. This would be useful in testing but also in investigating workarounds and whether we have to make customizations in Django.
Based off that we can make an assessment on different approaches to achieve this and conclude if Django should make this easier.
Then we can start looking at updates to Django 🙂
If you want to discuss your approach, I recommend moving to the ​Django forum as there is a wider audience who might be able to give you feedback there.
comment:11 by , 17 months ago
Follow up in the forum: ​https://forum.djangoproject.com/t/feature-request-discussion-custom-rendering-for-readonly-fields-in-admin/32009
(for those who come by this thread)
comment:13 by , 4 months ago
| Resolution: | needsinfo |
|---|---|
| Status: | closed → new |
Hello everyone,
I recently added a comment on the ​related Django forum thread regarding the ​removal of the workaround for `ReadOnlyPasswordHashWidget` that allowed custom widgets to be used for rendering readonly fields in the Django admin.
As a result of this change, it's no longer possible to customize the rendering of readonly fields using widgets. To demonstrate this, I’ve created a ​minimal project comparing Django 5.2 and the latest development version (installed from the main branch on GitHub).
Django 5.2: custom widget used for readonly field

Development version: default admin rendering used

At ​OpenWISP, we leveraged this workaround in Django <= 5.2.x to render the django-leaflet widget for users with view-only permissions. Moreover, we want to ​utilize this functionality to render properly formatted JSON in the Django admin.
This will need to be clear what we see and what we want to see instead.
I believe the above screenshots with the ​demo project make the intended outcome clear.
Since the workaround is no longer allowed, I urge the maintainers to reconsider this feature request.
comment:14 by , 4 months ago
| Summary: | feature request: custom rendering for readonly fields in admin → Custom rendering for readonly fields in admin |
|---|---|
| Triage Stage: | Unreviewed → Accepted |
comment:15 by , 4 months ago
Thank you Sarah for accepting the ticket. Now the question is whether we want to simply continue supporting the workaround or whether we want to provide an official cleaner way to implement this, although probably having both available initially could provide a backward compatibile upgrade path (eg: previous implementations will continue working, we can advise to migrate to the new way in the release notes and warn that the previous workaround is deprecated and will be removed in the next release).
comment:16 by , 3 days ago
Removing support for the read_only property on widgets (i.e. the "workaround" for ReadOnlyPasswordHashWidget) will break a number of things for me at $DAYJOB. We use the read_only property on a number of custom widgets. One use-case is very similar to that being discussed here, but that is not the only use case we have.
I urge you to revert that change immediately, until this is worked out.
For the long-term, I think ideally you should have a concept of a widget rendering read-only, similar to the concept of a widget rending disabled. Then, readonly_fields should render using that mechanism. Lastly, instead of the existing behavior where the "view" form puts all the fields in exclude and then something later adds back pseudo-fields using AdminReadonlyField, they should instead be readonly_fields, which would use the new read-only widget rendering. The behaviors of AdminReadonlyField would be merged into the stock widgets as their read-only rendering behavior. AdminReadonlyField would go away (probably with a deprecation period for backwards-compatibility).
Put differently, there are currently multiple different ways of handling fields being read-only. I propose to unify those all into a mode where the Widget can render in a read-write way (the default, and as exists today) or in read-only mode (using the behaviors from AdminReadonlyField).
I think having the Widget render read-write vs read-only makes more sense than the proposed readonly_formfield_overrides which requires separate Widget classes for read-write vs read-only.
Hi David.
This is interesting. I think I see where you're coming from but, could I ask you to put together an minimal project that demonstrates exactly the issue you're seeing — perhaps with some screenshots etc — so we can make sure we're 100% clear?
I'm not sure about an adjustment here. I think it would depend on exactly what's being proposed. Do you have a specific implementation idea?
It may be that there's a work-around.
bf39978a53f117ca02e9a0c78b76664a41a54745 introduced a check on the widget for a
read_onlyattribute, when renderingAdminReadonlyFieldif field in self.form.fields: widget = self.form[field].field.widget # This isn't elegant but suffices for contrib.auth's # ReadOnlyPasswordHashWidget. if getattr(widget, 'read_only', False): return widget.render(field, value)If you were to override,
ModelAdmin.get_form()to set a custom widget (withread_only=True) on the required field, when the user did not have thechangepermission, you should 🤔 be able to leverage this to get the behaviour you need. (If you could put together that test project it would be easy enough to have a play with this...)Given these questions, and that #14802 was closed as
wontfix, I'm going to close this asneedsinforight now. A sample project plus a proposal is probably needed to progress. With just a sample project, asking on the DevelopersMailingList might provide some help. Exploring the work-around would be the shortest route forward.