0

If I want to change how an object is added or changed in the Django admin UI, I can simply override the form (docs).

For a user with only "view" permission (no add, no change, no delete), that form doesn't even show up.

If I want to change how an object is displayed in the admin UI for a user with only "view" permission on the "detail page", what do I do?

P.S. When I say "detail page" I mean the page rendered when you click on the object in the list view. The URL ends in "change", but nothing can be changed for a user without change permission.

EDIT: Maybe change_form_template?

EDIT 2: A related way to ask this might be: how are read-only fields shown on the change form, and how do I customize a read-only field display? I think if you don't have "change" permission, it's rendering the fields as read-only.

EDIT 3: This answer says you can't customize read-only fields. So maybe this is not possible?

dfrankow
  • 20,191
  • 41
  • 152
  • 214
  • I think this is a dup of https://stackoverflow.com/questions/14832739/django-admin-how-to-display-widget-on-readonly-field – dfrankow Jul 19 '23 at 22:12
  • Well, not quite a dup. That question is trying to display a custom widget on a field declared readonly. This question is trying to display a custom widget on a field NOT declared readonly, but made readonly by the absence of the "change" permission. – dfrankow Jul 19 '23 at 22:23
  • I guess this functionality you have to build yourself. Because why would a user with only view permissions use the admin panel at all? It is way easier to view the details of an object via the "normal", open part of your app which is not the admin part. I hope that makes sense. You can definitely do it, but it requires a lot of overwriting of the admin templates. – Tarquinius Jul 20 '23 at 08:22
  • These users are staff, and it is useful for them to be able to see all our data in the admin, but they shouldn't be able to change anything. I think this is actually a pretty common use case in real applications. – dfrankow Jul 20 '23 at 15:52
  • I see. Does [this](https://stackoverflow.com/q/56521918/18018869) help you further? Maybe you could use [this hook](https://docs.djangoproject.com/en/4.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.change_view) to return the default logic when edit-permissions but return a custom detail-view templateresponse when read-only-permissions? That's what I would try – Tarquinius Jul 21 '23 at 07:25
  • 1
    Yes, thank you. That is the direction I was headed as well. If I get this to work, I'll post about it. – dfrankow Jul 21 '23 at 12:39

1 Answers1

0

I ended up:

  • Overloading change_view on my model admin. See docs. Within change_view I put widget_html in the extra_context.

  • Setting change_form_template to my template that uses the widget_html, something like this:

{% extends "admin/change_form.html" %}
{% block after_field_sets %}

    {% if not has_change_permission %}
        {# Display a read-only/disabled version of my widget #}
        <div class="form-row">
            <b>Field name:</b>{{widget_html}}
        </div>
    {% endif %}

{% endblock %}

Feels a bit hacky, but it worked.

Since my widget had editable parts, I render it with attrs={"disabled": True} in change_view:

if not self.has_change_permission(request):
    # Fetch the object, since self.model seems not bound?
    the_model = MyObject.objects.get(pk=object_id)
    widget_html = MyWidget().render(
        "field_name",
        the_model.the_field,
        # disable the widget so it is not editable
        attrs={"disabled": True},
    )
    extra_context |= {
        "widget_html": widget_html,
    }

That makes the UI not changeable (e.g., drop-downs don't drop down). It would be even better to make my widget react to a read-only flag and render differently, but I am moving fast and this does the trick.

dfrankow
  • 20,191
  • 41
  • 152
  • 214