在Django admin中显示外键列作为详细对象的链接

时间:2016-05-31 07:12:34

标签: python django django-admin

link-in-django-admin-to-foreign-key-object中所述,可以将ForeignKey字段显示为管理详细信息页面的链接。

总结一下,

class Foo(Model):
    bar = models.ForeignKey(Bar)

class FooAdmin(ModelAdmin):
    list_display = ('link_to_bar',)
    def link_to_bar(self, obj):
        link = urlresolvers.reverse('admin:app_bar_change', args=[obj.bar_id])
        return u'<a href="%s">%s</a>' % (link, obj.bar) if obj.bar else None
    link_to_bar.allow_tags = True

问题是:我们可以更自动地做到吗?例如,向FooAdmin定义提供一个外键列表,以显示为详细信息页面的链接:

class FooAdmin(ModelAdmin):
    ...
    list_foreign_key_links = ('bar',)
    ...

我知道这些ModelAdmin类是使用元类编程生成的。那么,它应该是可能的。什么是一个好的开始呢?

3 个答案:

答案 0 :(得分:12)

以下解决方案使用this answer 但可以在所有模型中重复使用,而无需在每个管理类中添加方法。

示例模型

# models.py
from django.db import models

class Country(models.Model):
    name = models.CharField(max_length=200)
    population = models.IntegerField()

class Career(models.Model):
    name = models.CharField(max_length=200)
    average_salary = models.IntegerField()

class Person(models.Model):
    name = models.CharField(max_length=200)
    age = models.IntegerField()
    country = models.ForeignKey(Country, on_delete=models.CASCADE)
    career = models.ForeignKey(Career, on_delete=models.CASCADE)

示例管理员

# admin.py
from django.utils.html import format_html
from django.urls import reverse

from .models import Person


def linkify(field_name):
    """
    Converts a foreign key value into clickable links.

    If field_name is 'parent', link text will be str(obj.parent)
    Link will be admin url for the admin url for obj.parent.id:change
    """
    def _linkify(obj):
        app_label = obj._meta.app_label
        linked_obj = getattr(obj, field_name)
        model_name = linked_obj._meta.model_name
        view_name = f"admin:{app_label}_{model_name}_change"
        link_url = reverse(view_name, args=[linked_obj.id])
        return format_html('<a href="{}">{}</a>', link_url, linked_obj)

    _linkify.short_description = field_name # Sets column name
    return _linkify



@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
    list_display = [
        "name",
        "age",
        linkify(field_name="country"),
        linkify(field_name="career"),
    ]

结果

给出一个名为app的应用程序,以及一个ID为Person(name='Adam' age=20)123的具有国家和职业外键值的Person实例456, 列表结果将是:

| Name | Age |                          Country                          |...|
|------|-----|-----------------------------------------------------------|...|
| Adam |  20 | <a href="/admin/app/country/123">Country object(123)</a>  |...|

(继续)

|...|                          Career                         |
|---|---------------------------------------------------------|
|...| <a href="/admin/app/career/456">Career object(456)</a>  |

答案 1 :(得分:1)

一个良好的开端是关注BaseModelAdminModelAdmin的来源。尝试了解ModelAdmin如何生成默认链接。 扩展ModelAdmin,添加一个方法来生成指向任意外键的链接,并查看ChangeList如何生成更改列表。

我还建议你使用format_html来渲染链接,这会使link_to_bar.allow_tags = True变得不必要:

from django.utils.html import format_html

class FooAdmin(ModelAdmin):
    list_display = ('link_to_bar', )
    def link_to_bar(self, obj):
        link = urlresolvers.reverse('admin:app_bar_change', args=[obj.bar_id])
        return format_html('<a href="{}">{}</a>', link, obj.bar) if obj.bar else None

答案 2 :(得分:0)

略微接受已接受的答案。它不一定更好,但可以在注释中实现一些建议:

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


def linkify(field_name):
    def _linkify(obj):
        content_type = ContentType.objects.get_for_model(obj)
        app_label = content_type.app_label
        linked_obj = getattr(obj, field_name)
        linked_content_type = ContentType.objects.get_for_model(linked_obj)
        model_name = linked_content_type.model
        view_name = f"admin:{app_label}_{model_name}_change"
        link_url = reverse(view_name, args=[linked_obj.pk])
        return format_html('<a href="{}">{}</a>', link_url, linked_obj)

    _linkify.short_description = field_name.replace("_", " ").capitalize()
    return _linkify