django-planet
Jan. 17, 2025

Django admin tip: Adding links to related objects in change forms

in blog Matthias Kestenholz: Posts about Django
original entry Django admin tip: Adding links to related objects in change forms

Django admin tip: Adding links to related objects in change forms

Any issue which came up on the Django Forum and Discord is how to add links to other objects to the Django administration interface. It’s something I’m doing often and I want to share the pattern I’m using.

It’s definitely not rocket science and there are probably better ways to do it, but this one works well for me.

Method 1: Override the change form template

In one project users can be the editor of exactly one organization. The link between organizations and users is achieved using a Editor model with a ForeignKey(Organization) and a OneToOneField(User).

I wanted to add a link to the organization page at the bottom of the user form. An easy way to achieve this is to add a template at templates/admin/auth/user/change_form.html (or something similar if you’re using a custom user model):

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

{% block after_related_objects %}
{{ block.super }}

{% if original.editor %}
<fieldset class="module aligned">
<h2>Organization</h2>
<div class="form-row">
  <a href="{% url 'admin:organizations_organization_change' original.editor.organization.pk %}">{{ original.editor.organization }}</a>
</div>
</fieldset>
{% endif %}

{% endblock after_related_objects %}

The original context variable contains the object being edited. The editor attribute is the reverse accessor for the OneToOneField mentioned above.

Method 2: Add a method to the model admin class returning a HTML blob

A terrible but also nice way is to add a method to the ModelAdmin class which returns the HTML containing the links you want, and adding the name of the method to readonly_fields. This is even mentioned in the official readonly_fields documentation but I discovered this by accident a few years back.

The method name doesn’t have to be added anywhere else, not to fields nor do you have to define fieldsets for this to work. Just adding it to readonly_fields appends it to the end of the form, before any eventual inlines you’re using.

from django.template.loader import render_to_string
from app import models

@admin.register(models.Class)
class ClassAdmin(admin.ModelAdmin):
    list_display = ["name", "language_code"]
    readonly_fields = ["admin_show_custom_districts"]

    @admin.display(description=_("districts")
    def admin_show_custom_districts(self, obj):
        return render_to_string(
            "admin/admin_show_custom_districts.html",
            {"custom_districts": obj.customdistrict_set.all()},
        )