Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding a button to Wagtail Dashboard

Tags:

django

wagtail

Is it possible to add a additional button(buttons) on the top panel as shown in the picture?

enter image description here

I did't find anything in Google and here.

like image 773
khashashin Avatar asked Dec 14 '22 20:12

khashashin


1 Answers

It is not clear on the screenshot whether you use modeladmin or the snippets to expose this model to the user but I'll assume the former.

I don't know about a hook which allows you to add buttons directly to the header, however the template uses blocks which should allow us to overwrite just this part.

We can take advantage of the resolution order of the templates and create templates/modeladmin/app-name/model-name/index.html which will take precedence over /modeladmin/index.html. So given with your app called feedler and a model called Entry, create /modeladmin/feedler/entry/index.html with the following content:

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

{% block header_extra %}
    <a href="#">My New Button</a>
    {{ block.super }}{% comment %}Display the original buttons {% endcomment %}
{% endblock %}

Right now your button doesn't do much. To create an action which will interact with that model admin, you'll need to create some button/url/permission helpers and a view.

Let's say the action is exporting the objects to a CSV file. Brace yourself, there's quite a bit of code ahead.

In /feedler/admin.py, create the button/url/permission helpers and view:

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)
        

In /feedler/wagtail_hooks.py, create and register the ModelAdmin:

from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register

from .admin import ExportModelAdminMixin
from .models import Entry

class EntryModelAdmin(ExportModelAdminMixin, ModelAdmin):
    model = Entry
    # ...
    
modeladmin_register(EntryModelAdmin)

With all that setup, you should be able to use {% include 'modeladmin/includes/button.html' with button=view.button_helper.export_button %} in the template created above.

like image 150
Loïc Teixeira Avatar answered Jan 05 '23 05:01

Loïc Teixeira