[英]django-admin: Add extra row with totals
我正在使用标准的 django 管理模块来显示行列表。 其中一列是数字字段。 我想显示一个额外的“总计”行,其中大部分列为空白,除了数字列,它应该是所有对象的总计。
在管理模块中是否有一种简单的方法可以做到这一点,或者我最好为它制作一个自定义视图?
我正在使用 Django 1.2。
是的,您可以通过多种方式做到这一点,但大多数 django-ist 的做法是:
首先覆盖默认的 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)
因此,您将列表视图上下文添加为“总计”,以保留您的总价值并将其传递给模板。
如果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 使用,如果更新相关模板可能会导致问题。
注意:
使用过滤器时,总数不会改变,请参阅下面的评论。
我认为 Django 的方法是覆盖 django 的管理应用程序使用的 ChangeList 类。 您可以在 django 1.2 中通过调用管理类中的get_changelist
方法来完成此操作。 在我的示例中: TomatoAdmin
调用此方法返回自定义 ChangeList calss。 这个变更列表类: 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 (您的 Admin 类和自定义 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 %}
我会让你决定如何按照你想要的方式设计它。
这个线程已经持续了一段时间,但我可能不是最后一个正在寻找这样一个功能的人。 因此,我创建了一个可以轻松添加总计的包。
结帐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)]
随意 fork 并改进它。
好的,所以有一种方法可以做到这一点,包括添加几个新的模板标签并扩展管理模板。
首先,在您的应用程序的templatetags
文件夹中,您创建一个admin_totals.py
文件,其中包含一个模板标签以创建总计行:
from django.template import Library
register = Library()
def totals_row(cl):
total_functions = getattr(cl.model_admin, 'total_functions', {})
totals = []
for field_name in cl.list_display:
if field_name in total_functions:
values = [getattr(i, field_name) for i in cl.result_list]
totals.append(total_functions[field_name](values))
else:
totals.append('')
return {'cl': cl, 'totals_row': totals}
totals_row = register.inclusion_tag("myapp/totals_row.html")(totals_row)
然后你需要myapp/totals_row.html
所述行的模板(无论你的模板在哪里):
<table id="result_totals">
<tfoot>
<tr>
{% for total in totals_row %}<td>{{ total }}</td>{% endfor %}
</tr>
</tfoot>
</table>
然后,您需要将其连接到从 Django 默认继承的自定义管理模板中,例如myapp/mymodel_admin.html
:
{% extends "admin/change_list.html" %}
{% load admin_totals %}
{% block result_list %}
{{ block.super }}
{% totals_row cl %}
{% endblock %}
最后,将其连接到应用程序admin.py
文件中的配置中:
class MyModelAdmin(ModelAdmin):
list_display = ('name', 'date', 'numerical_awesomeness')
total_functions = {'numerical_awesomeness': sum}
change_list_template = 'myapp/mymodel_admin.html'
这应该连接到新模型的自定义管理模板中,显示总计行。 如果您愿意,您还可以使用sum
以外的其他汇总函数对其进行扩展。
剩下的一小点:总计行实际上并不在结果表中,因为这需要对 Django 管理模板进行一些相当粗糙的复制和粘贴。 对于奖励积分,您可以在totals_row.html
文件的底部添加以下 JavaScript 代码:
<script type="text/javascript">
django.jQuery('#result_list').append(django.jQuery('#result_totals tfoot')[0])
django.jQuery('#result_totals').remove()
</script>
一个警告:所有这些只会反映当前显示的项目的总数,而不是所有现有项目的总数。 解决此问题的一种方法是在ModelAdmin
类list_per_page
设置为某个不可行的大数字,如果您不介意潜在的性能影响。
还有一个包“django-admin-changelist-stats”可以提供汇总统计信息。 我已经在 Django 1.6 项目中成功使用了它。
IMO 使用 c4urself 的解决方案进行细粒度模板自定义,但如果它只是您所追求的表的聚合(sum/avg/min/max),那么此解决方案非常好。
从它的网站
" 这个简单的应用程序为 Django 管理更改列表视图提供了统计和聚合功能。它允许以一种简单的方式在更改列表页面的末尾显示统计信息,只需向模型管理对象添加一个选项。"
更多信息,包括示例,请访问其 bitbucket 存储库网页https://bitbucket.org/frankban/django-admin-changelist-stats/src
要通过应用过滤器进行计算:
def changelist_view(self, request, extra_context=None):
extra_context = extra_context or {}
extra_context['total'] = sum([item.cash for item in self.get_queryset(request)])
return super().changelist_view(request, extra_context=extra_context)
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.filter(**request.GET.dict())
在最初的问题中,在评论中澄清说他们对当前页面上的所有对象感兴趣。 如果您对数据库中的所有对象感兴趣,但应用了过滤器,则可以采用类似于c4urself's answer的路线,但具有以下核心位:
from django.contrib import admin
from django.db.models import Sum
from tomatoes.tomato.models import CherryTomato
class TomatoAdmin(admin.ModelAdmin):
def get_changelist_instance(self, request):
cl = super().get_changelist_instance(request)
q = cl.get_queryset(request).aggregate(tomato_sum=Sum('num_in_box'))
cl.tomato_count = q['tomato_sum']
return cl
class Meta:
model = CherryTomato
list_display = ('name', 'num_in_box')
如果您创建一个覆盖管理视图的自定义视图,您应该能够将查询集与当前页面信息一起使用并将数据切片以创建适合当前页面的总数。 如果你想要一个真正的总数,我仍然会看到一个自定义覆盖的管理视图作为你正在寻找的选项。
AFAIK 如果不创建自定义视图,就无法做到这一点。 无论如何,这个概念有点缺陷,因为用户希望这样的行只显示可见对象的总数,而不是查询集中的所有对象。 因此,任何分页都会导致混乱。
顺便说一句,您可以通过执行以下操作向管理列表视图添加额外的列:
class ItemAdmin(admin.ModelAdmin):
model = Item
list_display = ('field1', 'field2', 'extra_field')
def extra_field(self, obj):
return u'%s' % do_something_with(obj)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.