django admin中指向外键对象的链接

django admin中指向外键对象的链接,django,django-admin,foreign-key-relationship,Django,Django Admin,Foreign Key Relationship,我有一辆a型车,有一把B型车的外键。 在Django admin中,如何在模型a的管理页面中,在打开模型B的管理页面的ForeignKey字段旁边添加链接?您可以执行以下操作: models.py(示例): 管理员 from django.core import urlresolvers class AAdmin(admin.ModelAdmin): list_display = ["field1","link_to_B"] def link_to_B(self, obj):

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

您可以执行以下操作:

models.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
从django.core导入URL解析程序
AAdmin类(admin.ModelAdmin):
列表显示=[“字段1”,“链接到”]
def链接到(自身、obj):
link=urlresolvers.reverse(“admin:yourapp_b_change”,args=[obj.b.id])#模型名称必须为小写
返回u“”%(链接,对象名称)
链接到。允许标记=真

用你的应用程序名称替换你的应用程序。

除了公认的答案,在较新版本的Django中,反向方法现在位于包Django.url(cf.)中

此外,您应该使用函数在管理员界面中输出HTML。然后,允许_标记将变得无用

最后,要添加到用户编辑页面的链接,我在
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'
从django.url反向导入
从django.utils.html导入格式\u html
类ObjectAdmin(admin.ModelAdmin):
列表显示=(“名称”,“链接到用户”)
def链接到用户(自身、obj):
link=reverse(“admin:auth\u user\u change”,args=[obj.user\u id])
返回格式为html(“”,链接,obj.user.username)
链接到用户。简短描述='编辑用户'

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

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',
    )
从django.url反向导入
从django.utils.html导入转义,标记_safe
@管理员注册(models.YourModel)
类YourModelAdmin(BaseModelAdmin):
def model_str(self,obj:models.YourModel):
link=reverse(“管理:模块模型更改”,参数=[obj.model\u id])
返回标记_安全(f“”)
model_str.short_description='model'
model_str.admin_order_字段='model'#使行可排序
列表显示=(
"model_str",,
)

现在有一个更简单的解决方案,将
相关的
作为要链接到的外键字段:

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

我创建了mixin,它对多对多关系做了类似的事情(在这里,它显示了相关对象的数量和到变更列表的链接,以及适当的过滤器)。根据我的主旨:


在django 1.1.x和1.0.x上测试[使用Python 3在django 1.8上测试]

如果你需要

  • 在详细信息页面上有指向FK的href链接(不在列表页面上)
  • 防止将选项/选项加载到详细信息页面上的FK小部件,并加快加载时间
步骤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)
步骤3:结果看起来像


万一有人也使用这个答案,您的模型在
reverse
函数中必须是小写的。那就是:
“admin:yourappëyourmodelèu change”
@Raphaël Gomès:感谢您的输入,编辑了答案。在Django 1.9+中不再需要-请查看FK项目旁边的铅笔图标。我只会添加
链接到B。简短的描述='B'
也会将列标题更改为B,而不是
链接到B
,感谢您的回答。我已经扩展了这个概念,但是可以跨模型重用:注意反向链接中使用的
auth
是模型所在的应用程序名。一般来说,它的形式应该是
admin:myapp\u mymodel\u change
也请注意-要使其工作,链接模型还需要有一个注册的管理员视图-即“admin.site.register(User)”-与User,这是正确的,但如果您使用的是自定义模型,提醒您也要在管理员中注册它的视图,否则反向功能将不起作用,因为它找不到app\u model\u change@bethlakshmi,我添加了一个提醒以检查您添加的评论。
list\u display\u links
创建到主对象的链接。在您的情况下,两个链接将是相同的。问题是要有两个不同的链接,一个链接到父对象,另一个链接到需要导入标记安全的相关对象从django.utils.html导入标记_safe``确定正确答案。请注意:可以使用“args=[obj.model_id]”并避免在SQL查询中使用无用的联接来提高查询性能
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)
@admin.register(UserProfile)
class UserProfileAdmin(CustomModelAdmin):
    fk_links = ['user',]