Adding a button to export to a csv/xlsx file on Wagtail Dashboard

Viewed 766

I am attempting the solution mentioned in this stack overflow post (Adding a button to Wagtail Dashboard) however the solution might be outdated, or at least it doesn't work for me and I'm unsure why.

Goal: Be able to export a object's data to csv

First, the button HTML code had to be slightly adjusted to be formatted correctly like so:

{% extends "modeladmin/index.html" %}
{% block header_extra %}
    <div class="right">
        <div class="addbutton" style="margin-left: 2em;">
            {% include "modeladmin/includes/button.html" with button=view.button_helper.export_button %}
            <a href="#" class="button bicolor icon icon-download">My button</a>
        </div>
    </div>
    {{ block.super }}{% comment %}Display the original buttons {% endcomment %}
{% endblock %}

and then I copied and pasted the views and helper functions:

from django.contrib.auth.decorators import login_required
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.utils.functional import cached_property
from django.utils.translation import ugettext as _
from wagtail.contrib.modeladmin.helpers import AdminURLHelper, ButtonHelper
from wagtail.contrib.modeladmin.views import IndexView


class ExportButtonHelper(ButtonHelper):
    """
    This helper constructs all the necessary attributes to create a button.
    
    There is a lot of boilerplate just for the classnames to be right :(
    """
    
    export_button_classnames = ['icon', 'icon-download']

    def export_button(self, classnames_add=None, classnames_exclude=None):
        if classnames_add is None:
            classnames_add = []
        if classnames_exclude is None:
            classnames_exclude = []

        classnames = self.export_button_classnames + classnames_add
        cn = self.finalise_classname(classnames, classnames_exclude)
        text = _('Export {}'.format(self.verbose_name_plural.title()))

        return {
            'url': self.url_helper.get_action_url('export', query_params=self.request.GET),
            'label': text,
            'classname': cn,
            'title': text,
        }


class ExportAdminURLHelper(AdminURLHelper):
    """
    This helper constructs the different urls.
    
    This is mostly just to overwrite the default behaviour
    which consider any action other than 'create', 'choose_parent' and 'index'
    as `object specific` and will try to add the object PK to the url
    which is not what we want for the `export` option.
    
    In addition, it appends the filters to the action.
    """

    non_object_specific_actions = ('create', 'choose_parent', 'index', 'export')

    def get_action_url(self, action, *args, **kwargs):
        query_params = kwargs.pop('query_params', None)

        url_name = self.get_action_url_name(action)
        if action in self.non_object_specific_actions:
            url = reverse(url_name)
        else:
            url = reverse(url_name, args=args, kwargs=kwargs)

        if query_params:
            url += '?{params}'.format(params=query_params.urlencode())

        return url

    def get_action_url_pattern(self, action):
        if action in self.non_object_specific_actions:
            return self._get_action_url_pattern(action)

        return self._get_object_specific_action_url_pattern(action)
        

class ExportView(IndexView):
    """
    A Class Based View which will generate 
    """
    
    def export_csv(self):
        data = self.queryset.all()
        response = ...
        return response
    
    
    @method_decorator(login_required)
    def dispatch(self, request, *args, **kwargs):
        super().dispatch(request, *args, **kwargs)
        return self.export_csv()

        
class ExportModelAdminMixin(object):
    """
    A mixin to add to your model admin which hooks the different helpers, the view
    and register the new urls.
    """

    button_helper_class = ExportButtonHelper
    url_helper_class = ExportAdminURLHelper

    export_view_class = ExportView

    def get_admin_urls_for_registration(self):
        urls = super().get_admin_urls_for_registration()
        urls += (
            url(
                self.url_helper.get_action_url_pattern('export'),
                self.export_view,
                name=self.url_helper.get_action_url_name('export')
            ),
        )

        return urls

    def export_view(self, request):
        kwargs = {'model_admin': self}
        view_class = self.export_view_class
        return view_class.as_view(**kwargs)(request)

and then I added the ModelAdmin to the hooks. I'm able to see the button however it doesn't function as it's supposed to (exporting the csv). In fact, it does absolutely nothing (the href is # after all) and I feel like I'm missing some steps.

1 Answers

I used the same implementation as you. I guess that you are having problems in the def export_csv(self) method here my implementation

from djqscsv.djqscsv import render_to_csv_response


class ExportView(IndexView):
    model_admin = None

    def export_csv(self) -> dict:
        if (self.model_admin is None) or not hasattr(
            self.model_admin, "csv_export_fields"
        ):
            data = self.queryset.all().values()
        else:
            data = self.queryset.all().values(*self.model_admin.csv_export_fields)
        return render_to_csv_response(data)

    @method_decorator(login_required)
    def dispatch(self, request: HttpRequest, *args: list, **kwargs: dict) -> dict:
        super().dispatch(request, *args, **kwargs)
        return self.export_csv()

csv_export_fields can be added to your model admin to specify which fields you want to export

here I am adding the HTML file:

{% extends "modeladmin/index.html" %}

{% block header_extra %}
    {% include 'modeladmin/includes/button.html' with button=view.button_helper.export_button %}
    {{ block.super }}
{% endblock %}

your admin.py

class MyModelAdmin(ModelAdmin, ExportModelAdminMixin): 
    model = MyModel 
    menu_icon = 'tag' 
    menu_order = 200 
    index_template_name = "wagtailadmin/export_csv.html"
    csv_export_fields = [ "field_name","field_name_1", ]
    list_display = ('first_name', 'last_name'') 
    search_fields = ('first_name', 'last_name',)
Related