36

我正在使用标准的 django 管理模块来显示行列表。其中一列是数字字段。我想显示一个额外的“总计”行,其中大部分列为空白,除了数字列,它应该是所有对象的总数。

在管理模块中是否有一种简单的方法可以做到这一点,或者我最好为它制作一个自定义视图?

我正在使用 Django 1.2。

4

9 回答 9

39

是的,您可以通过多种方式做到这一点,但大多数 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_templateModelAdmin.change_list_template. (谢谢你 c4urself)。是的,但是更改默认的 django 管理模板是一个非常糟糕的选择,因为它被许多其他 ModelAdmin 使用,并且如果更新相关模板可能会导致问题。



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

于 2012-01-06T13:53:09.217 回答
37

我认为 Django 的方法是覆盖 django 的管理应用程序使用的 ChangeList 类。您可以在 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(您的 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 %}

我将由您决定如何按照您想要的方式设置样式。

于 2012-01-05T15:27:25.870 回答
15

这个线程持续了一段时间,但我可能不是最后一个正在寻找这种功能的人。因此,我创建了一个可以轻松添加总计的包。

结帐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)]

随意分叉并改进它。

于 2017-12-27T20:57:26.953 回答
7

好的,所以有一种方法可以做到这一点,包括添加几个新的模板标签和扩展管理模板。

首先,在应用程序的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 管理模板进行一些相当粗糙的复制和粘贴。对于奖励积分,您可以在文件底部添加以下 JavaScripttotals_row.html代码:

<script type="text/javascript">
    django.jQuery('#result_list').append(django.jQuery('#result_totals tfoot')[0])
    django.jQuery('#result_totals').remove()
</script>

一个警告:所有这些都只会反映当前显示的项目的总数,而不是现有的所有项目。如果您不介意潜在的性能影响,那么解决此问题的一种方法是list_per_page在您的班级中设置一些不可行的大数字。ModelAdmin

于 2012-01-05T15:04:56.973 回答
4

还有一个包“django-admin-changelist-stats”可以提供汇总统计。我已在 Django 1.6 项目中成功使用它。

IMO 使用 c4urself 的解决方案进行细粒度的模板定制,但如果它只是聚合(总和/平均/最小/最大)的表,那么这个解决方案非常好。

从它的网站

" 这个简单的应用程序为 Django 管理更改列表视图提供了统计和聚合功能。它允许在更改列表页面的末尾以一种简单的方式显示统计信息,只需向模型管理对象添加一个选项。"

更多信息,包括示例,请访问其 bitbucket 存储库网页 https://bitbucket.org/frankban/django-admin-changelist-stats/src

于 2015-03-23T17:25:13.870 回答
1

在最初的问题中,评论中澄清说他们对当前页面上的所有对象都感兴趣。如果您对数据库中的所有对象感兴趣,但应用了过滤器,则可以采用类似于c4urself 的答案的路线,但具有以下核心位:

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')
于 2021-04-07T14:55:18.250 回答
1

通过应用过滤器进行计算:

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())
于 2021-05-19T22:16:42.247 回答
0

如果您创建一个覆盖管理视图的自定义视图,您应该能够将查询集与当前页面信息一起使用并对数据进行切片以创建适合当前页面的总数。如果您想要一个真实的总数,我仍然会看到自定义覆盖的管理视图作为您正在寻找的选项。

于 2011-12-18T03:09:04.330 回答
-3

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)
于 2011-12-17T02:31:43.850 回答