From 6b51655e86fdcb416ef7ff34fd267263f6ea88d3 Mon Sep 17 00:00:00 2001 From: Matt Westcott Date: Fri, 26 Jan 2024 17:11:31 +0000 Subject: [PATCH] Add a mixin for adding the import button back under Wagtail 6 --- README.md | 25 +++++++++++++++- tests/models.py | 15 ++++++++-- wagtail_airtable/mixins.py | 30 ++++++++++++++++++- .../wagtail_airtable/_import_button.html | 11 +++++++ 4 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 wagtail_airtable/templates/wagtail_airtable/_import_button.html diff --git a/README.md b/README.md index a00dce9..939021f 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ This package will attempt to match a model object against row in Airtable using * Install the package with `pip install wagtail-airtable` * Add `'wagtail_airtable'` to your project's `INSTALLED_APPS`. - * To enable the snippet-specific import button on the Snippet list view make sure `wagtail_airtable` is above `wagtail.snippets` in your `INSTALLED_APPS` + * On Wagtail 5.x, to enable the snippet-specific import button on the Snippet list view make sure `wagtail_airtable` is above `wagtail.snippets` in your `INSTALLED_APPS` * In your settings you will need to map Django models to Airtable settings. Every model you want to map to an Airtable sheet will need: * An `AIRTABLE_BASE_KEY`. You can find the base key in the [Airtable API docs](https://airtable.com/api) when you're signed in to Airtable.com * An `AIRTABLE_TABLE_NAME` to determine which table to connect to. @@ -252,6 +252,29 @@ The messaging will be off if you do this, so another setting has been made avail `WAGTAIL_AIRTABLE_PUSH_MESSAGE` - set this to whatever you'd like the messaging to be e.g. `WAGTAIL_AIRTABLE_PUSH_MESSAGE='Airtable save is happening in the background'` +### Adding an Import action to the snippet list view (Wagtail 6.x) + +As of Wagtail 6.0, the Import action is no longer automatically shown on the snippet listing view (although it is still available through Settings -> Airtable import). To add it back, first ensure that your snippet model is [registered with an explicit viewset](https://docs.wagtail.org/en/stable/topics/snippets/registering.html#using-register-snippet-as-a-function). Then, ensure that the index view for that viewset inherits from `SnippetImportActionMixin`: + +```python +from wagtail.snippets.models import register_snippet +from wagtail.snippets.views.snippets import IndexView, SnippetViewSet +from wagtail_airtable.mixins import SnippetImportActionMixin +from .models import Advert + + +class AdvertIndexView(SnippetImportActionMixin, IndexView): + pass + + +class AdvertViewSet(SnippetViewSet): + model = Advert + index_view_class = AdvertIndexView + +register_snippet(Advert, viewset=AdvertViewSet) +``` + + ### Trouble Shooting Tips #### Duplicates happening on import Ensure that your serializer matches your field definition *exactly*, and in cases of `CharField`'s that have `blank=True` or `null=True` setting `required=False` on the serializer is also important. diff --git a/tests/models.py b/tests/models.py index d7af988..96028e4 100644 --- a/tests/models.py +++ b/tests/models.py @@ -2,8 +2,9 @@ from wagtail.fields import RichTextField from wagtail.models import Page from wagtail.snippets.models import register_snippet +from wagtail.snippets.views.snippets import IndexView, SnippetViewSet -from wagtail_airtable.mixins import AirtableMixin +from wagtail_airtable.mixins import AirtableMixin, SnippetImportActionMixin class SimplePage(Page): @@ -14,7 +15,6 @@ class Publication(models.Model): title = models.CharField(max_length=30) -@register_snippet class Advert(AirtableMixin, models.Model): STAR_RATINGS = ( (1.0, "1"), @@ -77,6 +77,17 @@ def __str__(self): return self.title +class AdvertIndexView(SnippetImportActionMixin, IndexView): + pass + + +class AdvertViewSet(SnippetViewSet): + model = Advert + index_view_class = AdvertIndexView + +register_snippet(Advert, viewset=AdvertViewSet) + + @register_snippet class SimilarToAdvert(Advert): pass diff --git a/wagtail_airtable/mixins.py b/wagtail_airtable/mixins.py index 4a64398..ad48576 100644 --- a/wagtail_airtable/mixins.py +++ b/wagtail_airtable/mixins.py @@ -1,14 +1,16 @@ import sys -from importlib import import_module from ast import literal_eval from logging import getLogger from airtable import Airtable from django.conf import settings from django.db import models +from django.urls import reverse from django.utils.functional import cached_property from requests import HTTPError +from wagtail.admin.widgets.button import Button + from .tests import MockAirtable logger = getLogger(__name__) @@ -362,3 +364,29 @@ def delete(self, *args, **kwargs): class Meta: abstract = True + + +class ImportButton(Button): + template_name = "wagtail_airtable/_import_button.html" + + def get_context_data(self, parent_context): + context = super().get_context_data(parent_context) + context["csrf_token"] = parent_context["csrf_token"] + context["model_opts"] = parent_context["model_opts"] + context["next"] = parent_context["request"].path + return context + + +class SnippetImportActionMixin: + # Add a new action to the snippet listing page to import from Airtable + @cached_property + def header_buttons(self): + buttons = super().header_buttons + if issubclass(self.model, AirtableMixin) and self.add_url: + buttons.append( + ImportButton( + "Import from Airtable", + url=reverse("airtable_import_listing") + ) + ) + return buttons diff --git a/wagtail_airtable/templates/wagtail_airtable/_import_button.html b/wagtail_airtable/templates/wagtail_airtable/_import_button.html new file mode 100644 index 0000000..c68f2c6 --- /dev/null +++ b/wagtail_airtable/templates/wagtail_airtable/_import_button.html @@ -0,0 +1,11 @@ +{% load i18n wagtailadmin_tags %} + +{% blocktranslate asvar action_label with snippet_type_name=model_opts.verbose_name_plural %}Import {{ snippet_type_name }}{% endblocktranslate %} +
+ {% csrf_token %} + + + +