django-admin:添加带总计的额外行
我正在使用标准的Django管理模块来显示一系列数据行。其中有一列是数字类型的字段。我想添加一个额外的“总计”行,这一行的大部分列都留空,只有数字列需要显示所有对象的总和。
在管理模块中有没有简单的方法可以做到这一点,还是说我应该自己做一个自定义的视图来实现呢?
我使用的是Django 1.2。
9 个回答
这个讨论已经持续了一段时间了,但我想我不是最后一个在寻找这种功能的人。因此,我创建了一个包,可以很方便地添加总计。
可以查看一下 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)]
欢迎随意复制并改进它。
我觉得在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 %}
我就不管你怎么设计这个样式了,随你喜欢。
是的,你可以用很多方法来实现,但最符合 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 也在用这个模板,如果相关模板更新了,可能会引发问题。
注意:
使用过滤器时,总数不会改变,见下面的评论。