Django Admin 链接到相关对象

Posted

技术标签:

【中文标题】Django Admin 链接到相关对象【英文标题】:Django Admin linking to related objects 【发布时间】:2011-09-19 02:27:07 【问题描述】:

我的应用有创建页面的用户。在管理员的页面屏幕中,我想列出创建该页面的用户,并且在该列表中,我希望用户名具有指向管理员中的用户页面(而不是页面)的链接。

class PageAdmin(admin.ModelAdmin):
    list_display = ('name', 'user', )
    list_display_links = ('name','user',)
admin.site.register(Page, PageAdmin)

我希望通过将其作为 list_display 中的链接,它会默认链接到实际的用户对象,但它仍然会转到 Page。

我确定我在这里遗漏了一些简单的东西。

【问题讨论】:

【参考方案1】:

没有必要修改您的模型,这实际上是一种不好的做法(将特定于管理员的视图逻辑添加到您的模型中?糟糕!)在某些情况下甚至不可能。

幸运的是,这一切都可以从 ModelAdmin 类中实现:

from django.urls import reverse
from django.utils.safestring import mark_safe    


class PageAdmin(admin.ModelAdmin):
    # Add it to the list view:
    list_display = ('name', 'user_link', )
    # Add it to the details view:
    readonly_fields = ('user_link',)

    def user_link(self, obj):
        return mark_safe('<a href=""></a>'.format(
            reverse("admin:auth_user_change", args=(obj.user.pk,)),
            obj.user.email
        ))
    user_link.short_description = 'user'


admin.site.register(Page, PageAdmin)

编辑 2016-01-17: 更新了使用 make_safe 的答案,因为 allow_tags 现在已弃用。

2019-06-14 编辑: 更新了使用 django.urls 的答案,因为从 Django 1.10 开始,django.core.urls 已被弃用。

【讨论】:

谢谢你,比另一个更好的答案! (可能可以理解,因为 Django 自 2011 年以来已经发展了很多......) readonly_fields = ('user_link') 不是read_only_fields = ('user_link') 更好的obj.user.pk,以防主键不被称为id 可能想使用format_html('&lt;a......, mark_safe(reverse(...)), obj.user.email) 来转义&lt;a&gt; 的主体,以防你不信任它docs.djangoproject.com/en/2.0/ref/utils/… 这可以概括为允许list_display中的任何相关模型被超链接吗?【参考方案2】:

将此添加到您的模型中:

  def user_link(self):
      return '<a href="%s">%s</a>' % (reverse("admin:auth_user_change", args=(self.user.id,)) , escape(self.user))

  user_link.allow_tags = True
  user_link.short_description = "User" 

您可能还需要在models.py 的顶部添加以下内容:

  from django.template.defaultfilters import escape
  from django.core.urls import reverse

admin.py,在list_display,添加user_link

list_display = ('name', 'user_link', )

不需要list_display_links

【讨论】:

谢谢!澄清一下,将其添加到我的页面模型中? 是否可以在实际更改页面而不是列表视图上执行此操作? 那是另一个模型 "auth_user" + 下划线 + "change" - 所以如果另一个模型是 frobnicate - 它就是 frobnicate_change。 请...没有人这样做。 WhyNotHugo 的回答要干净得多。【参考方案3】:

现代版本 od django 需要使用 format_html

@admin.register(models.Foo)
class FooAdmin(admin.ModelAdmin):
    list_display = ('ts', 'bar_link',)

    def bar_link(self, item):
        from django.shortcuts import resolve_url
        from django.contrib.admin.templatetags.admin_urls import admin_urlname
        url = resolve_url(admin_urlname(models.Bar._meta, 'change'), item.bar.id)
        return format_html('<a href="url">name</a>'.format(url=url, name=str(item.bar)))

【讨论】:

别忘了添加“from django.utils.html import format_html”【参考方案4】:

我最终得到了一个简单的助手:

from django.shortcuts import resolve_url
from django.utils.safestring import SafeText
from django.contrib.admin.templatetags.admin_urls import admin_urlname
from django.utils.html import format_html


def model_admin_url(obj: Model, name: str = None) -> str:
    url = resolve_url(admin_urlname(obj._meta, SafeText("change")), obj.pk)
    return format_html('<a href=""></a>', url, name or str(obj))

然后你可以在你的模型管理员中使用帮助器:

class MyAdmin(admin.ModelAdmin):
    readonly_field = ["my_link"]

    def my_link(self, obj):
        return model_admin_url(obj.my_foreign_key)

【讨论】:

能补充一下使用方法吗? @HarryMoreno 确定,已添加【参考方案5】:

我的很多管理页面都需要这个,所以我为它创建了一个 mixin 来处理不同的用例:

pip install django-admin-relation-links

然后:

from django.contrib import admin
from django_admin_relation_links import AdminChangeLinksMixin


@admin.register(Group)
class MyModelAdmin(AdminChangeLinksMixin, admin.ModelAdmin):

    # ...

    change_links = ['field_name']

查看 GitHub 页面了解更多信息。试试看,告诉我效果如何!

https://github.com/gitaarik/django-admin-relation-links

【讨论】:

我试过了,它对我不起作用,可能是因为文档。 @filtfilt 你能在 GitHub 页面上打开一个问题来描述你的问题吗? @filtfilt 可能是因为继承顺序错误,你应该把AdminChangeLinksMixin放在第一位。自述文件有误,现在更新。 我添加了change_links = ['model_name'],但它仍然不起作用:(。我在内联中得到'model_name:-',但仅此而已。这可能是因为模型,我有我的@ 987654326@ 到,在不同的应用程序中。 @filtfilt 请在 GitHub 页面上打开一个问题,无需在 SO 上污染此答案;)【参考方案6】:

我决定制作一个看起来像这样的简单管理混入(请参阅文档字符串了解用法):

from django.contrib.contenttypes.models import ContentType
from django.utils.html import format_html
from rest_framework.reverse import reverse


class RelatedObjectLinkMixin(object):
    """    
    Generate links to related links. Add this mixin to a Django admin model. Add a 'link_fields' attribute to the admin
    containing a list of related model fields and then add the attribute name with a '_link' suffix to the
    list_display attribute. For Example a Student model with a 'teacher' attribute would have an Admin class like this:

    class StudentAdmin(RelatedObjectLinkMixin, ...):
        link_fields = ['teacher']

        list_display = [
            ...
            'teacher_link'
            ...
        ]
    """

    link_fields = []

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if self.link_fields:
            for field_name in self.link_fields:
                func_name = field_name + '_link'
                setattr(self, func_name, self._generate_link_func(field_name))

    def _generate_link_func(self, field_name):
        def _func(obj, *args, **kwargs):
            related_obj = getattr(obj, field_name)
            if related_obj:
                content_type = ContentType.objects.get_for_model(related_obj.__class__)
                url_name = 'admin:%s_%s_change' % (content_type.app_label, content_type.model)
                url = reverse(url_name, args=[related_obj.pk])
                return format_html('<a href="" class="changelink"></a>', url, str(related_obj))
            else:
                return None
        return _func

【讨论】:

这个你真的不需要ContentType;你可以使用obj._meta.model(或obj.__class__)。 return _func 之前粘贴_func.short_description = field_name,否则您的所有列都会有标题“FUNC”。这将保留原始名称。 这是一个在 Django 2.2 上运行的更新的 mixin,包括上述建议 gist.github.com/Vigrond/ac3c468377ce6d3e53f9b7059fd42569【参考方案7】:

如果有人尝试使用内联管理员执行此操作,请考虑自 Django 1.8 以来名为 show_change_link 的属性。

您的代码可能如下所示:

class QuestionInline(admin.TabularInline):
    model = Question
    extra = 1
    show_change_link = True


class TestAdmin(admin.ModelAdmin):
    inlines = (QuestionInline,)


admin.site.register(Test, TestAdmin)

这将为管理员的内联部分中的每个外键关系添加一个更改/更新链接。

【讨论】:

以上是关于Django Admin 链接到相关对象的主要内容,如果未能解决你的问题,请参考以下文章

通过 Django admin 删除对象时是不是可以禁用相关对象的生成?

Django-admin:如何在记录更改列表中显示指向对象信息页面的链接而不是编辑表单?

在 django admin 上编辑实际对象之前保存相关对象

Django Admin Cookbook-38如何获取特定对象的Django Admin后台URL

Django admin自定义字段按条件获取最新相关对象的结果

django admin 在 save_model 方法中删除内联对象