Django InlineModelAdmin:部分显示内联模型并链接到完整模型

32 投票
6 回答
24724 浏览
提问于 2025-04-15 18:21

我定义了几个模型:期刊、卷、卷的扫描信息等等。

一个期刊可以有多个卷,而一个卷可以有多个扫描信息。

我想要做的是:

  • 在期刊的管理页面上,我想要把卷的列表显示在一起(这个已经完成)
  • 把之前列表中的每个卷连接到它的管理页面,这样我就可以显示一个表单来编辑这个卷,并且把它的“扫描信息”列表也显示在一起。

所以我想要有这样的东西:

Journal #1 admin page
[name]
[publisher]
[url]
.....
list of volumes inline
    [volume 10] [..(other fields)..]   <a href="/link/to/volume/10">Full record</a>
    [volume 20] [..(other fields)..]   <a href="/link/to/volume/20">Full record</a>

然后

Volume #20 admin page
[volume number]
[..(other fields)...]
......
list of the scan info inline
    [scan info 33] [..(other fields)..]   <a href="/link/to/scaninfo/33">Full record</a>
    [scan info 44] [..(other fields)..]   <a href="/link/to/scaninfo/44">Full record</a>

我尝试做的是定义一个模型方法来生成代码,并试图在定义“卷内联”的管理类中使用它,但没有成功。

换句话说,

模型“卷”里面有类似这样的内容:

def selflink(self):
    return '<a href="/admin/journaldb/volume/%s/">Full record</a>' % self.vid
selflink.allow_tags = True

还有

class VolumeInline(admin.TabularInline):
    fields = ['volumenumber', 'selflink']
    model = Volume
    extra = 1

但是这给出了以下错误:

Exception Value: 'VolumeInline.fields' refers to field 'selflink' that is missing from the form.

有什么想法吗?

谢谢,

乔瓦尼

6 个回答

14

这里有一个可以重复使用的混合代码,它是基于其他一些回答制作的。这个混合代码很方便,因为它可以同时适用于表格形式和堆叠形式的内联显示,而且不会让你的模型或管理代码变得复杂。

# put this somewhere like admin_helpers.py
from django.core.urlresolvers import reverse

class InlineEditLinkMixin(object):
    readonly_fields = ['edit_details']
    edit_label = "Edit"
    def edit_details(self, obj):
        if obj.id:
            opts = self.model._meta
            return "<a href='%s' target='_blank'>%s</a>" % (reverse(
                'admin:%s_%s_change' % (opts.app_label, opts.object_name.lower()),
                args=[obj.id]
            ), self.edit_label)
        else:
            return "(save to edit details)"
    edit_details.allow_tags = True

# admin.py

class VolumeInline(InlineEditLinkMixin, admin.TabularInline):
    fields = ['foo', 'bar', 'edit_details']

class JournalAdmin(admin.ModelAdmin):
    inlines = [VolumeInline]

class ScanInfoInline(InlineEditLinkMixin, admin.StackedInline):
    fields = ['foo', 'bar', 'edit_details']

class JournalAdmin(admin.ModelAdmin):
    inlines = [ScanInfoInline]
25

更新:

从Django 1.8开始,这个功能现在是内置的了。

对于Django版本小于等于1.7的回答:

把你的代码放在models.py文件里,并使用条件语句:

def selflink(self):
    if self.id:
        return "<a href='/link/to/volume/%s' target='_blank'>Edit</a>" % str(self.id)
    else:
        return "Not present"

selflink.allow_tags = True

admin.py文件中,把selflink添加为只读字段:

class VolumeInline(admin.TabularInline):
    readonly_fields = ['selflink',]
    model = Volume

这个方法对我有效。

29

更新:从Django 1.8开始,这个功能已经内置了。

可以查看 这个回答官方文档

旧回答:

最后我找到了一种简单的解决方案。

我创建了一个新的模板,叫做 linked.html,它是 tabular.html 的一个副本,然后我添加了一段代码来创建链接。

{% if inline_admin_form.original.pk %}
          <td class="{{ field.field.name }}">
              <a href="/admin/{{ app_label }}/{{ inline_admin_formset.opts.admin_model_path }}/{{ inline_admin_form.original.pk }}/">Full record</a>
          </td>
{% endif %}

接着我创建了一个新的模型 LinkedInline,它继承自 InlineModelAdmin

#override of the InlineModelAdmin to support the link in the tabular inline
class LinkedInline(admin.options.InlineModelAdmin):
    template = "admin/linked.html"
    admin_model_path = None

    def __init__(self, *args):
        super(LinkedInline, self).__init__(*args)
        if self.admin_model_path is None:
            self.admin_model_path = self.model.__name__.lower()

然后当我定义一个新的内联时,只需要使用我的 LinkedInline,而不是普通的 InlineModelAdmin

希望这对其他人有帮助。

乔瓦尼

撰写回答