adding_reports.rst 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. Adding reports
  2. ==============
  3. Reports are views with listings of pages matching a specific query. They can also export these listings in spreadsheet format.
  4. They are found in the `Reports` submenu: by default, the `Locked Pages` report is provided, allowing an overview of locked pages on the site.
  5. It is possible to create your own custom reports in the Wagtail admin. Two base classes are provided:
  6. ``wagtail.admin.views.reports.ReportView``, which provides basic listing and spreadsheet export functionality, and
  7. ``wagtail.admin.views.reports.PageReportView``, which additionally provides a default set of fields suitable for page listings.
  8. For this example, we'll add a report which shows any pages with unpublished changes.
  9. .. code-block:: python
  10. # <project>/views.py
  11. from wagtail.admin.views.reports import PageReportView
  12. class UnpublishedChangesReportView(PageReportView):
  13. pass
  14. Defining your report
  15. ~~~~~~~~~~~~~~~~~~~~~
  16. The most important attributes and methods to customise to define your report are:
  17. .. method:: get_queryset(self)
  18. This retrieves the queryset of pages for your report. For our example:
  19. .. code-block:: python
  20. # <project>/views.py
  21. from wagtail.admin.views.reports import PageReportView
  22. from wagtail.core.models import Page
  23. class UnpublishedChangesReportView(PageReportView):
  24. def get_queryset(self):
  25. return Page.objects.filter(has_unpublished_changes=True)
  26. .. attribute:: template_name
  27. (string)
  28. The template used to render your report. For ``ReportView``, this defaults to ``"wagtailadmin/reports/base_report.html"``,
  29. which provides an empty report page layout; for ``PageReportView``, this defaults to
  30. ``"wagtailadmin/reports/base_page_report.html"`` which provides a listing based on the explorer views,
  31. displaying action buttons, as well as the title, time of the last update, status, and specific type of any pages.
  32. In this example, we'll change this to a new template in a later section.
  33. .. attribute:: title
  34. (string)
  35. The name of your report, which will be displayed in the header. For our example, we'll set it to
  36. ``"Pages with unpublished changes"``.
  37. .. attribute:: header_icon
  38. (string)
  39. The name of the icon, using the standard Wagtail icon names. For example, the locked pages view uses ``"locked"``,
  40. and for our example report, we'll set it to ``'doc-empty-inverse'``.
  41. Spreadsheet exports
  42. -------------------
  43. .. attribute:: list_export
  44. (list)
  45. A list of the fields/attributes for each model which are exported as columns in the spreadsheet view. For ``ReportView``, this
  46. is empty by default, and for ``PageReportView``, it corresponds to the listing fields: the title, time of the last update, status,
  47. and specific type of any pages. For our example, we might want to know when the page was last published, so we'll set
  48. ``list_export`` as follows:
  49. ``list_export = PageReportView.list_export + ['last_published_at']``
  50. .. attribute:: export_headings
  51. (dictionary)
  52. A dictionary of any fields/attributes in ``list_export`` for which you wish to manually specify a heading for the spreadsheet
  53. column, and their headings. If unspecified, the heading will be taken from the field ``verbose_name`` if applicable, and the
  54. attribute string otherwise. For our example, ``last_published_at`` will automatically get a heading of ``"Last Published At"``,
  55. but a simple "Last Published" looks neater. We'll add that by setting ``export_headings``:
  56. ``export_headings = dict(last_published_at='Last Published', **PageReportView.export_headings)``
  57. .. attribute:: custom_value_preprocess
  58. (dictionary)
  59. A dictionary of ``(value_class_1, value_class_2, ...)`` tuples mapping to ``{export_format: preprocessing_function}`` dictionaries,
  60. allowing custom preprocessing functions to be applied when exporting field values of specific classes (or their subclasses). If
  61. unspecified (and ``ReportView.custom_field_preprocess`` also does not specify a function), ``force_str`` will be used. To prevent
  62. preprocessing, set the preprocessing_function to ``None``.
  63. .. attribute:: custom_field_preprocess
  64. (dictionary)
  65. A dictionary of ``field_name`` strings mapping to ``{export_format: preprocessing_function}`` dictionaries,
  66. allowing custom preprocessing functions to be applied when exporting field values of specific classes (or their subclasses). This
  67. will take priority over functions specified in ``ReportView.custom_value_preprocess``. If unspecified (and
  68. ``ReportView.custom_value_preprocess`` also does not specify a function), ``force_str`` will be used. To prevent
  69. preprocessing, set the preprocessing_function to ``None``.
  70. Customising templates
  71. ---------------------
  72. For this example "pages with unpublished changes" report, we'll add an extra column to the listing template, showing the last
  73. publication date for each page. To do this, we'll extend two templates: ``wagtailadmin/reports/base_page_report.html``, and
  74. ``wagtailadmin/reports/listing/_list_page_report.html``.
  75. .. code-block:: html+django
  76. {# <project>/templates/reports/unpublished_changes_report.html #}
  77. {% extends 'wagtailadmin/reports/base_page_report.html' %}
  78. {% block listing %}
  79. {% include 'reports/include/_list_unpublished_changes.html' %}
  80. {% endblock %}
  81. {% block no_results %}
  82. <p>No pages with unpublished changes.</p>
  83. {% endblock %}
  84. .. code-block:: html+django
  85. {# <project>/templates/reports/include/_list_unpublished_changes.html #}
  86. {% extends 'wagtailadmin/reports/listing/_list_page_report.html' %}
  87. {% block extra_columns %}
  88. <th>Last Published</th>
  89. {% endblock %}
  90. {% block extra_page_data %}
  91. <td valign="top">
  92. {{ page.last_published_at }}
  93. </td>
  94. {% endblock %}
  95. Finally, we'll set ``UnpublishedChangesReportView.template_name`` to this new template: ``'reports/unpublished_changes_report.html'``.
  96. Adding a menu item and admin URL
  97. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  98. To add a menu item for your new report to the `Reports` submenu, you will need to use the ``register_reports_menu_item`` hook (see: :ref:`register_reports_menu_item`). To add an admin
  99. url for the report, you will need to use the ``register_admin_urls`` hook (see :ref:`register_admin_urls`). This can be done as follows:
  100. .. code-block:: python
  101. # <project>/wagtail_hooks.py
  102. from django.urls import path, reverse
  103. from wagtail.admin.menu import AdminOnlyMenuItem
  104. from wagtail.core import hooks
  105. from .views import UnpublishedChangesReportView
  106. @hooks.register('register_reports_menu_item')
  107. def register_unpublished_changes_report_menu_item():
  108. return AdminOnlyMenuItem("Pages with unpublished changes", reverse('unpublished_changes_report'), classnames='icon icon-' + UnpublishedChangesReportView.header_icon, order=700)
  109. @hooks.register('register_admin_urls')
  110. def register_unpublished_changes_report_url():
  111. return [
  112. path('reports/unpublished-changes/', UnpublishedChangesReportView.as_view(), name='unpublished_changes_report'),
  113. ]
  114. Here, we use the ``AdminOnlyMenuItem`` class to ensure our report icon is only shown to superusers. To make the report visible to all users,
  115. you could replace this with ``MenuItem``.
  116. The full code
  117. ~~~~~~~~~~~~~
  118. .. code-block:: python
  119. # <project>/views.py
  120. from wagtail.admin.views.reports import PageReportView
  121. from wagtail.core.models import Page
  122. class UnpublishedChangesReportView(PageReportView):
  123. header_icon = 'doc-empty-inverse'
  124. template_name = 'reports/unpublished_changes_report.html'
  125. title = "Pages with unpublished changes"
  126. list_export = PageReportView.list_export + ['last_published_at']
  127. export_headings = dict(last_published_at='Last Published', **PageReportView.export_headings)
  128. def get_queryset(self):
  129. return Page.objects.filter(has_unpublished_changes=True)
  130. .. code-block:: python
  131. # <project>/wagtail_hooks.py
  132. from django.urls import path, reverse
  133. from wagtail.admin.menu import AdminOnlyMenuItem
  134. from wagtail.core import hooks
  135. from .views import UnpublishedChangesReportView
  136. @hooks.register('register_reports_menu_item')
  137. def register_unpublished_changes_report_menu_item():
  138. return AdminOnlyMenuItem("Pages with unpublished changes", reverse('unpublished_changes_report'), classnames='icon icon-' + UnpublishedChangesReportView.header_icon, order=700)
  139. @hooks.register('register_admin_urls')
  140. def register_unpublished_changes_report_url():
  141. return [
  142. path('reports/unpublished-changes/', UnpublishedChangesReportView.as_view(), name='unpublished_changes_report'),
  143. ]
  144. .. code-block:: html+django
  145. {# <project>/templates/reports/unpublished_changes_report.html #}
  146. {% extends 'wagtailadmin/reports/base_page_report.html' %}
  147. {% block listing %}
  148. {% include 'reports/include/_list_unpublished_changes.html' %}
  149. {% endblock %}
  150. {% block no_results %}
  151. <p>No pages with unpublished changes.</p>
  152. {% endblock %}
  153. .. code-block:: html+django
  154. {# <project>/templates/reports/include/_list_unpublished_changes.html #}
  155. {% extends 'wagtailadmin/reports/listing/_list_page_report.html' %}
  156. {% block extra_columns %}
  157. <th>Last Published</th>
  158. {% endblock %}
  159. {% block extra_page_data %}
  160. <td valign="top">
  161. {{ page.last_published_at }}
  162. </td>
  163. {% endblock %}