django-admin:添加带总计的额外行

39 投票
9 回答
24807 浏览
提问于 2025-04-17 08:32

我正在使用标准的Django管理模块来显示一系列数据行。其中有一列是数字类型的字段。我想添加一个额外的“总计”行,这一行的大部分列都留空,只有数字列需要显示所有对象的总和。

在管理模块中有没有简单的方法可以做到这一点,还是说我应该自己做一个自定义的视图来实现呢?

我使用的是Django 1.2。

9 个回答

19

这个讨论已经持续了一段时间了,但我想我不是最后一个在寻找这种功能的人。因此,我创建了一个包,可以很方便地添加总计。

可以查看一下 https://github.com/douwevandermeij/admin-totals

使用方法:

from admin_totals.admin import ModelAdminTotals
from django.contrib import admin
from django.db.models import Sum, Avg

@admin.register(MyModel)
class MyModelAdmin(ModelAdminTotals):
    list_display = ['col_a', 'col_b', 'col_c']
    list_totals = [('col_b', Sum), ('col_c', Avg)]

欢迎随意复制并改进它。

39

我觉得在Django中,处理这个问题的方法是重写ChangeList类,这是Django的管理应用程序使用的。你可以在Django 1.2中通过在你的管理类中调用get_changelist方法来实现。在我的例子中:TomatoAdmin调用了这个方法,返回一个自定义的ChangeList类。这个ChangeList类:MyChangeList简单地在change_list.html的上下文中添加了一个额外的属性。

确保change_list.html文件放在以下目录中:

app_label/change_list.html

在这个例子中,就是:templates/admin/tomato/change_list.html

models.py(一个简单的模型,里面有一个整数字段)

class CherryTomato(models.Model):
    name = models.CharField(max_length=100)
    num_in_box = models.IntegerField()

admin.py(你的管理类和自定义的ChangeList类)

from django.contrib import admin
from django.contrib.admin.views.main import ChangeList
from django.db.models import Count, Sum

from tomatoes.tomato.models import CherryTomato

class MyChangeList(ChangeList):

    def get_results(self, *args, **kwargs):
        super(MyChangeList, self).get_results(*args, **kwargs)
        q = self.result_list.aggregate(tomato_sum=Sum('num_in_box'))
        self.tomato_count = q['tomato_sum']

class TomatoAdmin(admin.ModelAdmin):

    def get_changelist(self, request):
        return MyChangeList

    class Meta:
        model = CherryTomato

    list_display = ('name', 'num_in_box')

admin.site.register(CherryTomato, TomatoAdmin)

change_list.html(只需要相关的部分,复制/粘贴现有的并进行扩展)

  {% block result_list %}
      {% if action_form and actions_on_top and cl.full_result_count %}{% admin_actions %}{% endif %}
      {% result_list cl %}
      {% if action_form and actions_on_bottom and cl.full_result_count %}{% admin_actions %}{% endif %}
      Tomatoes on this page: {{ cl.tomato_count }}
  {% endblock %}

我就不管你怎么设计这个样式了,随你喜欢。

42

是的,你可以用很多方法来实现,但最符合 Django 风格的方法是:

首先,重写默认的 Django 列表视图... 然后给它一个新的模板文件目录。

ModelAdmin.changelist_view(self, request, extra_context=None)

比如:

class MyModelAdmin(admin.ModelAdmin):

    # A template for a very customized change view:
    change_list_template = 'admin/myapp/extras/sometemplate_change_form.html'

    def get_total(self):
        #functions to calculate whatever you want...
        total = YourModel.objects.all().aggregate(tot=Sum('total'))['tot']
        return total

    def changelist_view(self, request, extra_context=None):
        my_context = {
            'total': self.get_total(),
        }
        return super(MyModelAdmin, self).changelist_view(request,
            extra_context=my_context)

这样,你可以在你的列表视图上下文中添加一个 'total',用来保存总值,并把它传递给模板。

如果设置了 change_list_template,Django 就会使用这个模板;否则,它会使用默认的 Django 模板。

如果调用了 def changelist_view(self, request, extra_context=None),Django 会用这个函数来生成内容;否则,它会使用默认的 Django 视图。

接着,创建一个 admin/myapp/extras/sometemplate_change_form.html 文件,把你的 {{total}} 放在你想要的位置。

这里有一个关于如何 重写管理模板 的指南,还有如何 重写管理视图 的方法。

更新:我添加了一个简单的 aggregate 来计算总数,你可以根据需要进行修改。

更新 2:ModelAdmin 模板重写选项从 ModelAdmin.change_form_template 修正为 ModelAdmin.change_list_template。(感谢 c4urself)。不过,改变默认的 Django 管理模板其实不是个好主意,因为很多其他的 ModelAdmin 也在用这个模板,如果相关模板更新了,可能会引发问题。



注意:
使用过滤器时,总数不会改变,见下面的评论。

撰写回答