简体   繁体   English

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

[英]django-admin: Add extra row with totals

I'm using the standard django admin module to display a list of rows.我正在使用标准的 django 管理模块来显示行列表。 One of the columns is a numerical field.其中一列是数字字段。 I'd like to display an extra 'totals' row that has most of the columns as blank, except for the numerical column, which should be the total for all of the objects.我想显示一个额外的“总计”行,其中大部分列为空白,除了数字列,它应该是所有对象的总计。

Is there a simple way to do this within the admin module, or am I better off making a custom view for it?在管理模块中是否有一种简单的方法可以做到这一点,或者我最好为它制作一个自定义视图?

I'm using Django 1.2.我正在使用 Django 1.2。

Yes, you can do it in many ways, but most django-ist way to do is:是的,您可以通过多种方式做到这一点,但大多数 django-ist 的做法是:

First override the default django listing view... And give a new template file directory首先覆盖默认的 django 列表视图...并给出一个新的模板文件目录

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

Like:喜欢:

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)

So, you add your list view context a 'total' that keeps your total value and pass it to the template.因此,您将列表视图上下文添加为“总计”,以保留您的总价值并将其传递给模板。

if change_list_template will set, django uses that template, otherwise, it uses standard django template.如果change_list_template将设置,django 使用该模板,否则,它使用标准 django 模板。

If def changelist_view(self, request, extra_context=None) is called, django uses that function to create the content, otherwise it uses default django views.如果def changelist_view(self, request, extra_context=None)被调用,django 使用该函数来创建内容,否则它使用默认的 django 视图。

Then create a admin/myapp/extras/sometemplate_change_form.html file and place your {{total}} to any place you want.然后创建一个admin/myapp/extras/sometemplate_change_form.html文件并将您的{{total}}放在您想要的任何位置。

A guide to how to override admin templates And here is how to override admin views如何覆盖管理模板的指南这里是如何覆盖管理视图

UPDATE: I add a simple aggregate to calculate total.更新:我添加了一个简单的aggregate来计算总数。 you can edit it to set it as your needs.您可以编辑它以根据需要进行设置。

UPDATE 2 : ModelAdmin template override option fixed from ModelAdmin.change_form_template to ModelAdmin.change_list_template .更新 2 :ModelAdmin 模板覆盖选项从ModelAdmin.change_form_template固定到ModelAdmin.change_list_template (thank you c4urself). (谢谢 c4urself)。 Yes, but changing the default django admin template is a really bad choice, since it is used by many other ModelAdmin's and it might cause problems if related templates are updated.是的,但是更改默认的 django 管理模板是一个非常糟糕的选择,因为它被许多其他 ModelAdmin 使用,如果更新相关模板可能会导致问题。



NB :注意
The Total doesn't change when using filters, see comment below.使用过滤器时,总数不会改变,请参阅下面的评论。

I think the Django way to do this is to override the ChangeList class which django's admin app uses.我认为 Django 的方法是覆盖 django 的管理应用程序使用的 ChangeList 类。 You do this in django 1.2 by calling the get_changelist method in your admin class.您可以在 django 1.2 中通过调用管理类中的get_changelist方法来完成此操作。 In my example: TomatoAdmin calls this method returning a custom ChangeList calss.在我的示例中: TomatoAdmin调用此方法返回自定义 ChangeList calss。 This ChangeList class: MyChangeList simply adds an extra attribute to the context of change_list.html .这个变更列表类: MyChangeList只是增加了一个额外的属性的情况下change_list.html

Make sure that change_list.html is in the following directory:确保 change_list.html 位于以下目录中:

app_label/change_list.html

in the example this is: templates/admin/tomato/change_list.html在示例中,这是: templates/admin/tomato/change_list.html

models.py (A simple model with an integer field) models.py (一个带有整数字段的简单模型)

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

admin.py (your Admin class and a custom ChangeList class) 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 (relevant bit only, copy/paste the existing one and extend it) 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 %}

I'll leave it up to you how to style this the way you want it.我会让你决定如何按照你想要的方式设计它。

This thread is going on for a while, but I'm probably not the last one that is looking for such a feature.这个线程已经持续了一段时间,但我可能不是最后一个正在寻找这样一个功能的人。 Therefore, I created a package that makes it easy to add totals.因此,我创建了一个可以轻松添加总计的包。

Checkout https://github.com/douwevandermeij/admin-totals结帐https://github.com/douwevandermeij/admin-totals

Usage:用法:

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

Feel free to fork it and improve it.随意 fork 并改进它。

Ok, so there is a way to do this, involving adding in a couple of new template tags and extending the admin templates.好的,所以有一种方法可以做到这一点,包括添加几个新的模板标签并扩展管理模板。

First off, in your app's templatetags folder, you create an admin_totals.py file containing a template tag to create a totals row:首先,在您的应用程序的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)

Then you need the template for said row in myapp/totals_row.html (wherever your templates are):然后你需要myapp/totals_row.html所述行的模板(无论你的模板在哪里):

<table id="result_totals">
    <tfoot>
        <tr>
            {% for total in totals_row %}<td>{{ total }}</td>{% endfor %}
        </tr>
    </tfoot>
</table>

Then you need to wire that into a custom admin template inheriting from Django's default, such as myapp/mymodel_admin.html :然后,您需要将其连接到从 Django 默认继承的自定义管理模板中,例如myapp/mymodel_admin.html

{% extends "admin/change_list.html" %}

{% load admin_totals %}

{% block result_list %}
{{ block.super }}
{% totals_row cl %}
{% endblock %}

Finally, you wire that into the configuration in your admin.py file in your app:最后,将其连接到应用程序admin.py文件中的配置中:

class MyModelAdmin(ModelAdmin):

    list_display = ('name', 'date', 'numerical_awesomeness')
    total_functions = {'numerical_awesomeness': sum}

    change_list_template = 'myapp/mymodel_admin.html'

That should wire in the custom admin template for your new model, displaying the totals row.这应该连接到新模型的自定义管理模板中,显示总计行。 You can also extend it with other summary functions other than sum , should you so wish.如果您愿意,您还可以使用sum以外的其他汇总函数对其进行扩展。

One small point left: the totals row isn't actually in the results table, because that would require some fairly gnarly copying and pasting of the Django admin templates.剩下的一小点:总计行实际上并不在结果表中,因为这需要对 Django 管理模板进行一些相当粗糙的复制和粘贴。 For bonus points, you can add in the following smidge of JavaScript to the bottom of your totals_row.html file:对于奖励积分,您可以在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>

One caveat: all this will only reflect the totals for the items currently displayed, rather than for all items in existence.一个警告:所有这些只会反映当前显示的项目的总数,而不是所有现有项目的总数。 One way around this is to set list_per_page to some infeasibly large number on your ModelAdmin class, if you don't mind the potential performance hit.解决此问题的一种方法是在ModelAdminlist_per_page设置为某个不可行的大数字,如果您不介意潜在的性能影响。

There is also the package "django-admin-changelist-stats" that can give summary statistics.还有一个包“django-admin-changelist-stats”可以提供汇总统计信息。 I have successfully used it in a Django 1.6 project.我已经在 Django 1.6 项目中成功使用了它。

IMO use c4urself's solution for fine-grained template customization, but if its only aggregates (sum/avg/min/max) of tables you are after then this solution is pretty good. IMO 使用 c4urself 的解决方案进行细粒度模板自定义,但如果它只是您所追求的表的聚合(sum/avg/min/max),那么此解决方案非常好。

From its website从它的网站

" This simple application provides stats and aggregation capabilities for the Django admin changelist view. It allows to display stats at the end of the changelist page in a easy way, just adding one option to the model admin object. " " 这个简单的应用程序为 Django 管理更改列表视图提供了统计和聚合功能。它允许以一种简单的方式在更改列表页面的末尾显示统计信息,只需向模型管理对象添加一个选项。"

More information,including examples, at its bitbucket repository webpage https://bitbucket.org/frankban/django-admin-changelist-stats/src更多信息,包括示例,请访问其 bitbucket 存储库网页https://bitbucket.org/frankban/django-admin-changelist-stats/src

To calculate by applying the filters:要通过应用过滤器进行计算:

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())

In the original question it was clarified in a comment that they are interested in all the objects on the current page .在最初的问题中,在评论中澄清说他们对当前页面上的所有对象感兴趣。 If you are instead interested in all the objects in the database , but with filters applied, you could take a route similar to c4urself's answer , but with the following core bit:如果您对数据库中的所有对象感兴趣,但应用了过滤器,则可以采用类似于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')

if you create a custom view overiding the admin view you should be able to take the queryset along with the current page information and slice the data to create a total appropriate to the current page.如果您创建一个覆盖管理视图的自定义视图,您应该能够将查询集与当前页面信息一起使用并将数据切片以创建适合当前页面的总数。 if you want a true total than I still see a custom overridden admin view as the option you are looking for.如果你想要一个真正的总数,我仍然会看到一个自定义覆盖的管理视图作为你正在寻找的选项。

AFAIK there's no way to do this without creating a custom view. AFAIK 如果不创建自定义视图,就无法做到这一点。 The concept is a little flawed anyway, because a user would expect such a row to show the total for only the visible objects, rather than all the objects in the queryset.无论如何,这个概念有点缺陷,因为用户希望这样的行只显示可见对象的总数,而不是查询集中的所有对象。 Thus, any pagination would result in confusion.因此,任何分页都会导致混乱。

As an aside, you can add extra columns to an admin list view by doing something like the following:顺便说一句,您可以通过执行以下操作向管理列表视图添加额外的

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM