在 django admin 中链接到外键对象

Posted

技术标签:

【中文标题】在 django admin 中链接到外键对象【英文标题】:Link in django admin to foreign key object 【发布时间】:2015-05-04 04:03:03 【问题描述】:

我有一个模型 A 和模型 B 的外键。 在 Django admin 中,如何在模型 A 的管理页面中在打开模型 B 的管理页面的 ForeignKey 字段旁边添加一个链接?

【问题讨论】:

【参考方案1】:

您可以执行以下操作:

models.py(示例):

model B(models.Model):
    name = models.CharField(max_length=20)

model A(models.Model):
    field1 = models.CharField(max_length=20)
    Bkey = models.ForeignKey(B)

admin.py

from django.core import urlresolvers

class AAdmin(admin.ModelAdmin):
    list_display = ["field1","link_to_B"]
    def link_to_B(self, obj):
        link=urlresolvers.reverse("admin:yourapp_b_change", args=[obj.B.id]) #model name has to be lowercase
        return u'<a href="%s">%s</a>' % (link,obj.B.name)
    link_to_B.allow_tags=True

将 yourapp 替换为您的应用名称。

【讨论】:

以防万一有人也使用此答案,您的模型在 reverse 函数中必须为小写。那将是:"admin:yourapp_yourmodel_change". @Raphaël Gomès:感谢您的输入,编辑了答案。 在 Django 1.9+ 中不再需要 - 请参阅 FK 项目旁边的铅笔图标。 我只会添加link_to_B.short_description = 'B' 也会将列标题更改为B 而不是link to B 是否有无需明确列出所有字段的解决方案,即list_display = [f.name for f in MyModel._meta.fields]?也就是说,是否可以将所有外键显示为链接?【参考方案2】:

除了已接受的答案之外,在较新版本的 Django 中,reverse 方法现在位于包 django.urls 中(参见this link)。

此外,您应该使用 format_html 函数在管理中输出 html。那么 allow_tags 就变得没用了。

最后,添加一个用户编辑页面的链接,我在admin.py有这个功能:

from django.urls import reverse
from django.utils.html import format_html


class ObjectAdmin(admin.ModelAdmin):
    list_display = ('name', 'link_to_user')

    def link_to_user(self, obj):
        link = reverse("admin:auth_user_change", args=[obj.user_id])
        return format_html('<a href="">Edit </a>', link, obj.user.username)
    link_to_user.short_description = 'Edit user'

别忘了检查 cmets,有一些注意事项需要考虑。

【讨论】:

感谢您的回答。我扩展了这个概念,但以一种可跨模型重用的方式:***.com/a/53092940/4411196 请注意,反向链接中使用的auth 是模型所在的应用名称。一般来说,格式应该是admin:myapp_mymodel_change 另请注意 - 要使其工作,链接模型还需要具有注册的管理视图 - 即“admin.site.register(User)” - 与用户,这是真的,但如果你是使用自定义模型,提醒也要在管理员中注册它的视图,否则反向功能将不起作用,因为它找不到 app_model_change 谢谢@bethlakshmi,我已经添加了一个提醒,以检查您添加的 cmets。【参考方案3】:

Django 2.0+ 和 Python 3.5+:

from django.urls import reverse
from django.utils.html import escape, mark_safe

@admin.register(models.YourModel)
class YourModelAdmin(BaseModelAdmin):
    def model_str(self, obj: models.YourModel):
        link = reverse("admin:module_model_change", args=[obj.model_id])
        return mark_safe(f'<a href="link">escape(obj.model.__str__())</a>')

    model_str.short_description = 'Model'
    model_str.admin_order_field = 'model' # Make row sortable

    list_display = (
        'model_str',
    )

【讨论】:

需要导入mark_safe。 ``` from django.utils.html import mark_safe ``` 绝对正确的答案。请注意:使用 'args=[obj.model_id]' 可以提高查询性能并避免 SQL 查询中的无用联接【参考方案4】:

如果需要

详细信息页面(不在列表页面上)有指向 FK 页面的 href 链接 通过阻止将选项/选项加载到 FK 小部件来加快加载时间

Code for Django 3.2 for classes FKLinkWidget and CustomModelAdmin


[下面的代码在 Django 1.8 和 Python 3 上测试]

第 1 步:定义基本管理员助手

class FKLinkWidget(forms.TextInput):
    """Widget to show html link for FK field instead of default option field"""

    NO_VALUE_TEXT = 'None'

    def __init__(self, attrs=None):
        self.app_label = None
        self.model_name = None
        self.pk = None
        self.repr = None
        super().__init__(attrs)

    def set_obj(self, obj):
        self.app_label = obj._meta.app_label
        self.model_name = obj._meta.model_name
        self.pk = obj.pk
        self.repr = str(obj)

    def render(self, name, value, attrs=None):
        if self.pk:
            view_name = f"admin:self.app_label_self.model_name_change"
            link_url = reverse(view_name, args=[self.pk])
            return format_html('<a href="" target="_blank"></a>', link_url, self.repr)
        else:
            return format_html(value or self.NO_VALUE_TEXT)


class CustomModelAdmin(admin.ModelAdmin):
    """extendable ModelAdmin which provides several custom attributes

    - fk_links = list of FK fields that should be shown as read-only links on detail page
    this can prevent loading all choice options by django admin, which results 504 http error

    """
    fk_links = []

    def __init__(self, model, admin_site):
        super().__init__(model, admin_site)
        intersect = set(self.fk_links).intersection(self.readonly_fields + self.raw_id_fields)
        if intersect:
            raise ValueError(f'CustomModelAdmin fields: intersect are in readonly or raw_id fields')

    def get_form(self, request, obj=None, **kwargs):
        self.obj = obj
        form = super().get_form(request, obj, **kwargs)
        return form

    def formfield_for_dbfield(self, db_field, **kwargs):
        if db_field.name in self.fk_links:
            kwargs['widget'] = FKLinkWidget

        formfield = super().formfield_for_dbfield(db_field, **kwargs)

        if db_field.name in self.fk_links:
            # we disable any actions for that field
            if self.obj:
                fk = getattr(self.obj, db_field.name)
                if fk:
                    formfield.widget.widget.set_obj(fk)

            formfield.widget.can_add_related = False
            formfield.widget.can_change_related = False
            formfield.widget.can_delete_related = False
        return formfield

    def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
        if db_field.name in self.fk_links:
            kwargs["queryset"] = db_field.rel.to._default_manager.none()
            kwargs["required"] = False
        return super().formfield_for_foreignkey(db_field, request, **kwargs)

第 2 步:使用 CustomModelAdmin 作为管理模型的基类

@admin.register(UserProfile)
class UserProfileAdmin(CustomModelAdmin):
    fk_links = ['user',]

第 3 步:结果如下所示

【讨论】:

【参考方案5】:

今天有一个更简单的解决方案,related 是要链接到的外键字段:

class YourModelAdmin(model.modelAdmin):
    list_display = ["field_one", "field_two", "related"]
    list_display_links = ["field_one", "related"]

【讨论】:

list_display_links 创建一个指向主对象的链接。在您的情况下,两个链接都是相同的。问题是有 2 个不同的链接 field_one - 到父对象,related - 到相关对象【参考方案6】:

我创建了 mixin,它对多对多关系做了类似的事情(它显示了相关对象的数量和到带有适当过滤器的更改列表的链接)。基于我从中分叉的要点:

https://gist.github.com/hovi/2e3a216ecc4be685ec9e0d23b0eb7901

在 django 1.1.x 和 1.0.x 上测试

【讨论】:

以上是关于在 django admin 中链接到外键对象的主要内容,如果未能解决你的问题,请参考以下文章

如果 RowID 链接到外键,则隐藏 Gridview 中的删除按钮 [关闭]

在 django admin 中使用外键访问另一个模型字段

在搜索字段中正确使用外键引用,Django admin

Django Admin 自定义外键选择框

Django admin禁用外键下拉但保留添加按钮旁边

django admin中的外键表单字段