【问题标题】:Django Admin linking to related objectsDjango Admin 链接到相关对象
【发布时间】: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。

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

【问题讨论】:

    标签: django django-admin


    【解决方案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)
    

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

    【讨论】:

      猜你喜欢
      • 2015-05-04
      • 1970-01-01
      • 2020-08-24
      • 1970-01-01
      • 1970-01-01
      • 2023-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多