在Django Admin中将只读ForeignKey字段渲染为链接

18 投票
3 回答
9029 浏览
提问于 2025-04-16 13:51

我在Django的后台管理系统中有一个模型,这个模型使用了ModelAdmin的readonly_fields列表来显示一个“用户”字段,这个字段是一个外键,链接到Django的用户模型。默认情况下,readonly_fields会把这个字段显示为简单的文本,内容是用户的邮箱(比如说 someuser@domain.com)。我想知道怎么才能把它改成一个链接,点击后可以直接跳转到那个用户的管理页面?(比如说 <a href="/admin/auth/user/123/">someuser@domain.com</a>

3 个回答

0

我创建了一些混合样式:

# base.py
from django.urls import reverse
from django.utils.html import format_html

def base_method_factory(field_name):
    def base_method(obj):
        try:
            model = getattr(
                obj,
                field_name,
            )
            app_label = model._meta.app_label
            model_name = model._meta.model_name
        except AttributeError as e:
            return None
        return format_html(
            '<a href="{0}">{1}</a>&nbsp;',
            reverse('admin:%s_%s_change' % (app_label, model_name), args=[model.id]),
            str(model),
        )

    return base_method

class ReadonlyLinkMixin(object):
    def __init__(self, *args, **kwargs):
        self.create_linked_fields()
        super().__init__(*args, **kwargs)

    def create_linked_fields(self):
        linked_fields = filter(lambda name: name.endswith('_linked'), self.readonly_fields)
        for linked_field in linked_fields:
            setattr(
                self,
                linked_field,
                base_method_factory(linked_field.rpartition('_')[0])
            )

# models.py

class SomeModel(models.Model):
    ...
    task = models.FK(...)

# some_app_admin.py

@admin.register(SomeModel)
class SomeModelAdmin(
    ReadonlyLinkMixin,
    admin.ModelAdmin,
):
    fields = (
        ...
        'task_linked',
    )
    readonly_fields = (
        ...
        'task_linked',
    )

所以在管理页面上,它会显示成一个链接的字符串。

2

Cerin的回答在我的Django 1.8项目中不管用,因为readonly_fields里面不应该写字符串'user_link',而是要写一个已经定义好的函数的引用:

class ProfileAdmin(admin.ModelAdmin):
    # https://docs.djangoproject.com/en/1.8/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_display
    def user(obj):
        change_url = reverse('admin:auth_user_change', args=(obj.user.id,))
        return mark_safe('<a href="{0}">{1}</a>'.format(change_url, obj.user.email))
    user.short_description = 'Edit linked user'
    raw_id_fields = ('user',)
    list_display = (user, 'last_name', 'first_name', 'specialization',)
    list_display_links = ('first_name', 'last_name', ,)
    readonly_fields = (user, 'user',)

注意,user(obj)这个函数同时覆盖了Profile.user字段的单个模型编辑和模型列表显示。还要注意,readonly_fields里面同时使用了user和'user',一个是显示带链接的邮箱,另一个是显示用户名。

40

深入研究源代码后,我发现其实你可以在自己的ModelAdmin子类中定义一些字段,就像定义方法一样。而且,如果你想让这些字段显示成链接,只需要在方法中返回链接的HTML代码就可以了。

比如:

from django.contrib import admin
from django.utils.safestring import mark_safe
from django.core import urlresolvers

class MyModelAdmin(admin.ModelAdmin):

    readonly_fields = ['user_link']

    def user_link(self, obj):
        change_url = urlresolvers.reverse('admin:auth_user_change', args=(obj.user.id,))
        return mark_safe('<a href="%s">%s</a>' % (change_url, obj.user.email))
    user_link.short_description = 'User'

撰写回答