如何将一个对象的Django管理页面中的链接添加到相关对象的管理页面?

agf*_*agf 27 python django django-templates django-models django-admin

为了解决django-admin中缺少嵌套内联的问题,我将特殊情况放入两个模板中,以便在管理员更改页面和两个模型的内联管理员之间创建链接.

我的问题是:如何创建一个链接,从管理员更改页面或内联管理员的一个模型到管理员更改页面或相关模型的内联管理员干净利落,模板中没有讨厌的黑客?

我想要一个通用的解决方案,我可以应用于任何模型的管理员更改页面或内联管理员.


我有一个模型,post(不是它的真实姓名)既是blog管理页面上的内联,也有自己的管理页面.它不能仅仅是内联的原因是它具有带有外键的模型,只有在用它编辑时才有意义,并且只有在编辑时才有意义blog.

对于post管理页面,我更改了"fieldset.html"的一部分:

{% if field.is_readonly %}
    <p>{{ field.contents }}</p>
{% else %}
    {{ field.field }}
{% endif %}
Run Code Online (Sandbox Code Playgroud)

{% if field.is_readonly %}
    <p>{{ field.contents }}</p>
{% else %}
    {% ifequal field.field.name "blog" %}
        <p>{{ field.field.form.instance.blog_link|safe }}</p>
    {% else %}
        {{ field.field }}
    {% endifequal %}
{% endif %}
Run Code Online (Sandbox Code Playgroud)

创建指向blog管理页面的链接,其中blog_link是模型上的方法:

def blog_link(self):
      return '<a href="%s">%s</a>' % (reverse("admin:myblog_blog_change",  
                                        args=(self.blog.id,)), escape(self.blog))
Run Code Online (Sandbox Code Playgroud)

我找不到id了的blog实例以外的任何地方field.field.form.instance.

blog管理页面上,post内联的位置,我从以下位置修改了"stacked.html"的一部分:

<h3><b>{{ inline_admin_formset.opts.verbose_name|title }}:</b>&nbsp;
<span class="inline_label">{% if inline_admin_form.original %}
    {{ inline_admin_form.original }}
{% else %}#{{ forloop.counter }}{% endif %}</span>
Run Code Online (Sandbox Code Playgroud)

<h3><b>{{ inline_admin_formset.opts.verbose_name|title }}:</b>&nbsp;
<span class="inline_label">{% if inline_admin_form.original %}
    {% ifequal inline_admin_formset.opts.verbose_name "post" %}
    <a href="/admin/myblog/post/{{ inline_admin_form.pk_field.field.value }}/">
            {{ inline_admin_form.original }}</a>
{% else %}{{ inline_admin_form.original }}{% endifequal %}
{% else %}#{{ forloop.counter }}{% endif %}</span>
Run Code Online (Sandbox Code Playgroud)

创建一个指向post管理页面的链接,因为在这里我能够找到id存储在外键字段中的内容.


我确信有更好,更通用的方法来添加管理表单的链接而不重复自己; 它是什么?

Mik*_*bov 15

使用readonly_fields:

class MyInline(admin.TabularInline):
    model = MyModel
    readonly_fields = ['link']

    def link(self, obj):
        url = reverse(...)
        return mark_safe("<a href='%s'>edit</a>" % url)

    # the following is necessary if 'link' method is also used in list_display
    link.allow_tags = True
Run Code Online (Sandbox Code Playgroud)


bit*_*nik 15

Django 1.8中的新功能:show_change_link用于内联管理员.

在内联模型中将show_change_link设置为True(默认为False),以便内联对象具有指向其更改表单的链接(它们可以具有自己的内联).

from django.contrib import admin

class PostInline(admin.StackedInline):
    model = Post
    show_change_link = True
    ...

class BlogAdmin(admin.ModelAdmin):
    inlines = [PostInline]
    ...

class ImageInline(admin.StackedInline):
    # Assume Image model has foreign key to Post
    model = Image
    show_change_link = True
    ...

class PostAdmin(admin.ModelAdmin):
    inlines = [ImageInline]
    ...

admin.site.register(Blog, BlogAdmin)
admin.site.register(Post, PostAdmin)
Run Code Online (Sandbox Code Playgroud)


agf*_*agf 10

这是我目前的解决方案,基于Pannu(在他的编辑中)和Mikhail的建议.

我有一些顶级管理员更改视图,我需要链接到相关对象的顶级管理员更改视图,以及一些内联管理员更改视图,我需要链接到顶级管理员更改视图同一个对象.因此,我想分析链接方法,而不是为每个管理员更改视图重复它的变体.

我使用类装饰器来创建link可调用的,并将其添加到readonly_fields.

def add_link_field(target_model = None, field = '', link_text = unicode):
    def add_link(cls):
        reverse_name = target_model or cls.model.__name__.lower()
        def link(self, instance):
            app_name = instance._meta.app_label
            reverse_path = "admin:%s_%s_change" % (app_name, reverse_name)
            link_obj = getattr(instance, field, None) or instance
            url = reverse(reverse_path, args = (link_obj.id,))
            return mark_safe("<a href='%s'>%s</a>" % (url, link_text(link_obj)))
        link.allow_tags = True
        link.short_description = reverse_name + ' link'
        cls.link = link
        cls.readonly_fields = list(getattr(cls, 'readonly_fields', [])) + ['link']
        return cls
    return add_link
Run Code Online (Sandbox Code Playgroud)

如果您需要以某种方式获取链接文本而不是仅仅调用unicode要链接的对象,也可以传递自定义可调用对象.

我这样使用它:

# the first 'blog' is the name of the model who's change page you want to link to
# the second is the name of the field on the model you're linking from
# so here, Post.blog is a foreign key to a Blog object. 
@add_link_field('blog', 'blog')
class PostAdmin(admin.ModelAdmin):
    inlines = [SubPostInline, DefinitionInline]
    fieldsets = ((None, {'fields': (('link', 'enabled'),)}),)
    list_display = ('__unicode__', 'enabled', 'link')

# can call without arguments when you want to link to the model change page
# for the model of an inline model admin.
@add_link_field()
class PostInline(admin.StackedInline):
    model = Post
    fieldsets = ((None, {'fields': (('link', 'enabled'),)}),)
    extra = 0
Run Code Online (Sandbox Code Playgroud)

当然,如果我可以在管理员更改页面上嵌入管理员更改视图SubPostDefinition内部管理员内部而不修补Django ,那么这一切都不是必需的.PostBlog


Tom*_*cek 10

我认为agf的解决方案非常棒 - 给他带来很多赞誉.但我需要更多功能:

  • 能够为一个管理员提供多个链接
  • 能够链接到不同的应用程序中的模型

解:

def add_link_field(target_model = None, field = '', app='', field_name='link',
                   link_text=unicode):
    def add_link(cls):
        reverse_name = target_model or cls.model.__name__.lower()
        def link(self, instance):
            app_name = app or instance._meta.app_label
            reverse_path = "admin:%s_%s_change" % (app_name, reverse_name)
            link_obj = getattr(instance, field, None) or instance
            url = reverse(reverse_path, args = (link_obj.id,))
            return mark_safe("<a href='%s'>%s</a>" % (url, link_text(link_obj)))
        link.allow_tags = True
        link.short_description = reverse_name + ' link'
        setattr(cls, field_name, link)
        cls.readonly_fields = list(getattr(cls, 'readonly_fields', [])) + \
            [field_name]
        return cls
    return add_link
Run Code Online (Sandbox Code Playgroud)

用法:

# 'apple' is name of model to link to
# 'fruit_food' is field name in `instance`, so instance.fruit_food = Apple()
# 'link2' will be name of this field
@add_link_field('apple','fruit_food',field_name='link2')
# 'cheese' is name of model to link to
# 'milk_food' is field name in `instance`, so instance.milk_food = Cheese()
# 'milk' is the name of the app where Cheese lives
@add_link_field('cheese','milk_food', 'milk')
class FoodAdmin(admin.ModelAdmin):
    list_display = ("id", "...", 'link', 'link2')
Run Code Online (Sandbox Code Playgroud)

我很抱歉这个例子太不合逻辑了,但我不想使用我的数据.

  • 很棒的改进!很高兴的人正在使用/改进这一点. (2认同)