django-admin: добавить дополнительную строку с итогами
Я использую стандартный модуль администратора django для отображения списка строк. Один из столбцов является числовым полем. Я хотел бы отобразить дополнительную строку "итоги", в которой большинство столбцов пустые, за исключением числового столбца, который должен быть общим для всех объектов.
Есть ли простой способ сделать это в модуле администратора, или мне лучше сделать для него настраиваемое представление?
Я использую Django 1.2.
9 ответов
Да, вы можете сделать это разными способами, но большинство способов сделать это:
Сначала переопределите стандартное представление списка 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 и может вызвать проблемы при обновлении связанных шаблонов.
NB:
Общая сумма не изменяется при использовании фильтров, см. Комментарий ниже.
Я думаю, что способ 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 (ваш класс 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)]
Не стесняйтесь раскошелиться и улучшить его.
Итак, есть способ сделать это, добавив пару новых тегов шаблонов и расширив шаблоны администратора.
Во-первых, в вашем приложении 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. Для получения бонусных баллов вы можете добавить следующий фрагмент JavaScript в нижней части вашего totals_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
класс, если вы не возражаете против потенциального удара производительности.
Существует также пакет "django-admin-changelist-stats", который может предоставить сводную статистику. Я успешно использовал его в проекте Django 1.6.
IMO использует решение c4urself для детальной настройки шаблона, но если вам нужны только те таблицы (сумма /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 , но со следующим основным битом:
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)