Django:在管理页面上显示反向ForeignKey查找,作为只读列表?

时间:2014-04-14 21:22:30

标签: django django-admin

我想在Django管理页面上显示反向ForeignKey查找,并将其设置为只读,作为轻量级字符串列表。

执行此操作的标准方法似乎是使用内联管理字段,但我不希望它是可编辑的,因此我想知道是否有更轻量级的方法来执行此操作。

这些是我的字段:

class Book: 
  title = models.TextField(blank=True)
  author = models.ForeignKey(Author, on_delete=models.PROTECT)

class Author: 
  name = models.TextField(blank=True)

在作者的管理员更改/删除页面上,我想显示他们图书的只读列表。

我可以使用admin.StackedInline执行此操作,但将其设为只读非常麻烦:

class BooksInline(admin.StackedInline):
  model = Book
  fields = ('title',)
  readonly_fields = ('title',)

  def has_add_permission(request, obj):
    return False

  def has_delete_permission(request, obj, self):
    return False

结果列表占用了页面上的大量空间,因为设计期望它全部可编辑。

是否有更简单的方法来制作只读列表?

3 个答案:

答案 0 :(得分:8)

我不喜欢在模型中使用仅管理逻辑的想法,如果我不需要,我不喜欢使用模板进行混淆的想法,所以我在管理员中实现了这一点。 / p>

from django.contrib import admin

class AuthorAdmin(admin.ModelAdmin):
    readonly_fields = ('books_list')
    fields = ('name', 'books_list')

    def books_list(self, obj):
        books = Book.objects.filter(author=obj)
        if books.count() == 0:
            return '(None)'
        output = ', '.join([unicode(book) for book in books])
        return output
    books_list.short_description = 'Book(s)'

对于奖励积分,我们可以将每本书链接到该书的更改页面。

from django.contrib import admin
from django.core import urlresolvers
from django.utils.html import format_html

class AuthorAdmin(admin.ModelAdmin):
    readonly_fields = ('books_list')
    fields = ('name', 'books_list')

    def books_list(self, obj):
        books = Book.objects.filter(author=obj)
        if books.count() == 0:
            return '(None)'
        book_links = []
        for book in books:
            change_url = urlresolvers.reverse('admin:myapp_book_change', args=(book.id,))
            book_links.append('<a href="%s">%s</a>' % (change_url, unicode(book))
        return format_html(', '.join(book_links))
    books_list.allow_tags = True
    books_list.short_description = 'Book(s)'

双重奖励,您可以将其封装到一个函数中,这样您每次要在Admin中显示反向外键对象列表时都不必重写此逻辑:

from django.contrib import admin
from django.core import urlresolvers
from django.utils.html import format_html

def reverse_foreignkey_change_links(model, get_instances, description=None, get_link_html=None, empty_text='(None)'):
    if not description:
        description = model.__name__ + '(s)'

    def model_change_link_function(_, obj):
        instances = get_instances(obj)
        if instances.count() == 0:
            return empty_text
        output = ''
        links = []
        for instance in instances:
            change_url = urlresolvers.reverse('admin:%s_change' % model._meta.db_table, args=(instance.id,))
            links.append('<a href="%s">%s</a>' % (change_url, unicode(instance))
        return format_html(', '.join(links))

    model_change_link_function.short_description = description
    model_change_link_function.allow_tags = True
    return model_change_link_function

class AuthorAdmin(admin.ModelAdmin):
    readonly_fields = ('books_list')
    fields = ('name', 'books_list')

    def books_list = reverse_foreignkey_change_links(Book, lambda obj: Book.objects.filter(author=obj))

答案 1 :(得分:0)

我会使用django docs中提到的类方法,并使用文档here中描述的_set相关反向查找。

列表结果是只读的,因此无需在管理员中进行检查。

// models.py
class Book: 
  title = models.TextField(blank=True)
  author = models.ForeignKey(Author, on_delete=models.PROTECT)

class Author: 
  name = models.TextField(blank=True)

  def get_author_books(self):
      cur_books = self.book_set.all()
      books = [b.title for b in cur_books]
      return books

  get_author_books.short_description = "Author's Books"


// admin.py
class BooksInline(admin.StackedInline):
  model = Book
  fields = ('title','get_author_books')

答案 2 :(得分:0)

我通常会通过覆盖管理员模板来完成此操作。那是因为这主要是关于表示逻辑(但您可能希望在ModelAdmin中操纵查询集以使用prefetch_related())。创建文件myapp/templates/admin/myapp/mymodel/change_form.html。在文件中,输入:

{% extends "admin/change_form.html" %}
{% load i18n admin_materials %}

{% block content %}
{% if change %}
<div class="author-list">
    <h1>{% trans "Authors" %}</h1>
    <ul>
    {% for author in original.author_set.all %}
        <li>{{ author.name }}</li>
    {% endfor %}
    </ul>
</div>
{% endif %}

{{ block.super }}

{% endblock %}

这与标准的django.contrib.admin和django-suit兼容......我不能说像django-grappelli这样的异域管理皮肤会修改管理模板块名称。