将django admin链接到外键对象

时间:2015-03-03 13:21:07

标签: django django-admin foreign-key-relationship

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

6 个答案:

答案 0 :(得分:29)

您可以执行以下操作:

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替换为您的应用名称。

答案 1 :(得分:18)

除了已接受的答案之外,在较新版本的Django(1.10,1.11和2.0)中,反向方法现在位于 django.urls 包中(cf 。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'

答案 2 :(得分:2)

Django 2.0+和Python 3.5 +:

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

@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',
    )

答案 3 :(得分:0)

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

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

答案 4 :(得分:0)

我创建了mixin,它执行了与多对多关系类似的操作(其中显示了相关对象的数量,并使用适当的过滤器链接到变更列表)。基于我从中摘录的要点:

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

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

答案 5 :(得分:0)

[在Django 1.8和Python 3上进行了测试]

如果需要

  • 详细信息页面(不在列表页面上)上具有指向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)

步骤2:将CustomModelAdmin用作管理模型的基类

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

第3步:结果看起来像

enter image description here