Opened 5 years ago

Closed 5 months ago

Last modified 5 months ago

#30577 closed New feature (needsinfo)

feature request: 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: Unreviewed
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 (12)

comment:1 by Carlton Gibson, 5 years ago

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_only attribute, when rendering AdminReadonlyField

if 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 (with read_only=True) on the required field, when the user did not have the change permission, 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 as needsinfo right 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.

Last edited 5 years ago by Carlton Gibson (previous) (diff)

comment:2 by Carlton Gibson, 5 years ago

Resolution: → needsinfo
Status: new → closed

comment:3 by Florian Demmer, 3 years ago

Cc: Florian Demmer added

comment:4 by Filipe, 3 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...

Last edited 3 years ago by Filipe (previous) (diff)

comment:5 by Carlos Palol, 3 years ago

Cc: Carlos Palol added

comment:6 by ldeluigi, 5 months ago

Cc: ldeluigi 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.

in reply to:  6 comment:7 by Mariusz Felisiak, 5 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 Mariusz Felisiak, 5 months ago

Cc: Mariusz Felisiak added

in reply to:  6 comment:9 by ldeluigi, 5 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 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

Version 2, edited 5 months ago by ldeluigi (previous) (next) (diff)

comment:10 by Sarah Boyce, 5 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:12 by Sarah Boyce, 5 months ago

#35544 is a duplicate

Note: See TracTickets for help on using tickets.
Back to Top