kopia lustrzana https://github.com/wagtail/wagtail
Add spreadsheet export functionality to ModelAdmin
rodzic
877675bafb
commit
8e88f3535c
wagtail
admin/views
contrib/modeladmin
|
@ -1,4 +1,6 @@
|
|||
import csv
|
||||
import datetime
|
||||
|
||||
from xlsxwriter.workbook import Workbook
|
||||
|
||||
from collections import OrderedDict
|
||||
|
@ -51,7 +53,10 @@ class SpreadsheetExportMixin:
|
|||
|
||||
def write_xlsx_row(self, worksheet, row_dict, row_number):
|
||||
for col_number, (field, value) in enumerate(row_dict.items()):
|
||||
preprocess_function = self.custom_xlsx_field_preprocess.get(field, force_str)
|
||||
if not isinstance(value, (datetime.date, datetime.time)):
|
||||
preprocess_function = self.custom_xlsx_field_preprocess.get(field, force_str)
|
||||
else:
|
||||
preprocess_function = self.custom_xlsx_field_preprocess.get(field)
|
||||
processed_value = preprocess_function(value) if preprocess_function else value
|
||||
worksheet.write(row_number, col_number, processed_value)
|
||||
|
||||
|
@ -143,7 +148,6 @@ class LockedPagesView(ReportView):
|
|||
title = _('Locked Pages')
|
||||
header_icon = 'locked'
|
||||
export_heading_overrides = {'latest_revision_created_at': _("Updated"), 'status_string': _("Status"), 'content_type.model_class._meta.verbose_name.title': _("Type")}
|
||||
custom_xlsx_field_preprocess = {'latest_revision_created_at': None, 'locked_at': None}
|
||||
list_export = ['title', 'latest_revision_created_at', 'status_string', 'content_type.model_class._meta.verbose_name.title', 'locked_at', 'locked_by']
|
||||
|
||||
def get_queryset(self):
|
||||
|
|
|
@ -74,6 +74,7 @@ class ModelAdmin(WagtailRegisterable):
|
|||
menu_order = None
|
||||
list_display = ('__str__',)
|
||||
list_display_add_buttons = None
|
||||
list_export = ()
|
||||
inspect_view_fields = []
|
||||
inspect_view_fields_exclude = []
|
||||
inspect_view_enabled = False
|
||||
|
@ -202,6 +203,13 @@ class ModelAdmin(WagtailRegisterable):
|
|||
return self.list_display_add_buttons or self.get_list_display(
|
||||
request)[0]
|
||||
|
||||
def get_list_export(self, request):
|
||||
"""
|
||||
Return a sequence containing the fields/method output to be displayed
|
||||
in spreadsheet exports.
|
||||
"""
|
||||
return self.list_export
|
||||
|
||||
def get_empty_value_display(self, field_name=None):
|
||||
"""
|
||||
Return the empty_value_display value defined on ModelAdmin
|
||||
|
|
|
@ -24,13 +24,13 @@
|
|||
{% block search %}{% search_form %}{% endblock %}
|
||||
</div>
|
||||
{% block header_extra %}
|
||||
{% if user_can_create %}
|
||||
<div class="right">
|
||||
<div class="addbutton">
|
||||
<div class="right">
|
||||
{% if user_can_create %}
|
||||
<span class="addbutton">
|
||||
{% include 'modeladmin/includes/button.html' with button=view.button_helper.add_button %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
</div>
|
||||
</header>
|
||||
|
@ -86,6 +86,13 @@
|
|||
</nav>
|
||||
{% endblock %}
|
||||
|
||||
{% if view.list_export %}
|
||||
<div class="nice-padding col9">
|
||||
<a href="?export=base&{{ request.GET.urlencode }}" class="button bicolor icon icon-download">{% trans 'Download all' %}</a>
|
||||
<a href="?export=base&{{ request.GET.urlencode }}" class="button bicolor icon icon-download">{% trans 'Download filtered' %}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -24,6 +24,7 @@ from django.views.generic import TemplateView
|
|||
from django.views.generic.edit import FormView
|
||||
|
||||
from wagtail.admin import messages
|
||||
from wagtail.admin.views.reports import SpreadsheetExportMixin
|
||||
|
||||
from .forms import ParentChooserForm
|
||||
|
||||
|
@ -212,14 +213,15 @@ class InstanceSpecificView(WMABaseView):
|
|||
return super().get_context_data(**context)
|
||||
|
||||
|
||||
class IndexView(WMABaseView):
|
||||
class IndexView(SpreadsheetExportMixin, WMABaseView):
|
||||
|
||||
ORDER_VAR = 'o'
|
||||
ORDER_TYPE_VAR = 'ot'
|
||||
PAGE_VAR = 'p'
|
||||
SEARCH_VAR = 'q'
|
||||
ERROR_FLAG = 'e'
|
||||
IGNORED_PARAMS = (ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR)
|
||||
EXPORT_VAR = 'export'
|
||||
IGNORED_PARAMS = (ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, EXPORT_VAR)
|
||||
|
||||
# sortable_by is required by the django.contrib.admin.templatetags.admin_list.result_headers
|
||||
# template tag as of Django 2.1 - see https://docs.djangoproject.com/en/2.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.sortable_by
|
||||
|
@ -231,12 +233,14 @@ class IndexView(WMABaseView):
|
|||
if not self.permission_helper.user_can_list(request.user):
|
||||
raise PermissionDenied
|
||||
|
||||
self.list_export = self.model_admin.get_list_export(request)
|
||||
self.list_display = self.model_admin.get_list_display(request)
|
||||
self.list_filter = self.model_admin.get_list_filter(request)
|
||||
self.search_fields = self.model_admin.get_search_fields(request)
|
||||
self.items_per_page = self.model_admin.list_per_page
|
||||
self.select_related = self.model_admin.list_select_related
|
||||
self.search_handler = self.model_admin.get_search_handler(request, self.search_fields)
|
||||
self.export = (request.GET.get(self.EXPORT_VAR))
|
||||
|
||||
# Get search parameters from the query string.
|
||||
try:
|
||||
|
@ -249,12 +253,27 @@ class IndexView(WMABaseView):
|
|||
del self.params[self.PAGE_VAR]
|
||||
if self.ERROR_FLAG in self.params:
|
||||
del self.params[self.ERROR_FLAG]
|
||||
if self.EXPORT_VAR in self.params:
|
||||
del self.params[self.EXPORT_VAR]
|
||||
|
||||
self.query = request.GET.get(self.SEARCH_VAR, '')
|
||||
|
||||
if self.export == 'base':
|
||||
return self.as_spreadsheet(self.get_base_queryset(request=request))
|
||||
|
||||
self.queryset = self.get_queryset(request)
|
||||
|
||||
if self.export == 'current':
|
||||
return self.as_spreadsheet(self.queryset)
|
||||
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_heading(self, queryset, field):
|
||||
heading_override = self.export_heading_overrides.get(field)
|
||||
if heading_override:
|
||||
return force_str(heading_override)
|
||||
return force_str(label_for_field(field, model=self.model))
|
||||
|
||||
@property
|
||||
def media(self):
|
||||
return forms.Media(
|
||||
|
|
Ładowanie…
Reference in New Issue