diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3c1a080..70360f0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -26,18 +26,6 @@ jobs: strategy: matrix: include: - - python: '3.7' - django: 'Django>=3.2,<3.3' - wagtail: 'wagtail>=4.1,<4.2' - experimental: false - - python: '3.7' - django: 'Django>=3.2,<3.3' - wagtail: 'wagtail>=4.2,<4.3' - experimental: false - - python: '3.7' - django: 'Django>=3.2,<3.3' - wagtail: 'wagtail>=5.0,<5.1' - experimental: false - python: '3.9' django: 'Django>=4.2,<4.3' wagtail: 'wagtail>=5.0,<5.1' @@ -50,6 +38,22 @@ jobs: django: 'Django>=4.2,<4.3' wagtail: 'wagtail>=5.1,<5.2' experimental: false + - python: '3.11' + django: 'Django>=4.2,<4.3' + wagtail: 'wagtail>=5.2,<5.3' + experimental: false + - python: '3.11' + django: 'Django>=4.2,<4.3' + wagtail: 'wagtail>=6.0,<6.1' + experimental: false + - python: '3.11' + django: 'Django>=4.2,<4.3' + wagtail: 'wagtail>=6.1,<6.2' + experimental: false + - python: '3.11' + django: 'Django>=4.2,<4.3' + wagtail: 'wagtail>=6.2,<6.3' + experimental: false - python: '3.11' django: 'git+https://github.com/django/django.git@main#egg=Django' wagtail: 'git+https://github.com/wagtail/wagtail.git@main#egg=wagtail' diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..388612a --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,11 @@ +# 0.2.0 - 2024-09-03 + +* remove support of wagtail < 5.0 +* remove support of django < 4.2 +* add support of wagtail 6.0, 6.1, 6.2 +* fix usage of wagtail fields overrides registry +* add this CHANGELOG 😸 + +# 0.1.x - 2023-09-18 + +First version 🧀 🐦 diff --git a/README.md b/README.md index c13d5ab..9d44e4a 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ register_snippet(FoodAdmin) # or for an usage with wagtail-modeladmin: -from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register +from wagtail_modeladmin.options import ModelAdmin, modeladmin_register from wagtail_parler.handlers import ParlerModelAdminMixin from .models import Food diff --git a/docs/source/usage.md b/docs/source/usage.md index b7c9f41..2bc71f4 100644 --- a/docs/source/usage.md +++ b/docs/source/usage.md @@ -87,7 +87,7 @@ register_snippet(FoodAdmin) # Or for ModelAdmin: -from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register +from wagtail_modeladmin.options import ModelAdmin, modeladmin_register from .models import Food class FoodAdmin(ModelAdmin): @@ -122,7 +122,7 @@ register_snippet(FoodAdmin) # or for an usage with wagtail-modeladmin: -from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register +from wagtail_modeladmin.options import ModelAdmin, modeladmin_register from wagtail_parler.handlers import ParlerModelAdminMixin from .models import Food @@ -199,8 +199,8 @@ It will be used as a template to generate all languages tabs. from wagtail.admin.panels import FieldPanel from wagtail.admin.panels import ObjectList -from wagtail.contrib.modeladmin.options import ModelAdmin -from wagtail.contrib.modeladmin.options import modeladmin_register +from wagtail_modeladmin.options import ModelAdmin +from wagtail_modeladmin.options import modeladmin_register from wagtail_parler.handlers import ParlerModelAdminMixin from wagtail_parler.handlers import TranslationsList diff --git a/setup.cfg b/setup.cfg index 3ad5813..06c12eb 100644 --- a/setup.cfg +++ b/setup.cfg @@ -31,8 +31,8 @@ packages = find: include_package_data = true zip_safe = false install_requires = - Django>=3.2 - wagtail>=4.1 + Django>=4.2 + wagtail>=5.0 django-parler>=2.3 [options.extras_require] diff --git a/tox.ini b/tox.ini index d670c93..a5569ab 100644 --- a/tox.ini +++ b/tox.ini @@ -1,8 +1,7 @@ [tox] envlist = - py37-django32-wagtail{41,42,50}, - py39-django42-wagtail{50,51}, - py311-django42-wagtail51, + py39-django42-wagtail{50,51} + py311-django42-wagtail{51,52,60,61,62} py{39,311}-django42-wagtailmain qa @@ -11,19 +10,23 @@ commands = coverage run -a ./runtests.py basepython = - py37: python3.7 py39: python3.9 py311: python3.11 qa: python3.9 deps = - django32: Django>=3.2,<3.3 django42: Django>=4.2,<4.3 wagtail41: wagtail>=4.1,<4.2 wagtail42: wagtail>=4.2,<4.3 wagtail50: wagtail>=5.0,<5.1 wagtail51: wagtail>=5.1,<5.2 + wagtail52: wagtail>=5.2,<5.3 + wagtail60: wagtail>=6.0,<6.1 + wagtail61: wagtail>=6.1,<6.2 + wagtail62: wagtail>=6.2,<6.3 wagtailmain: https://github.com/wagtail/wagtail/archive/main.tar.gz + wagtail{41,42,50,51,52}: wagtail-modeladmin>=1.0,<2.0 + wagtail{60,61,62,main}: wagtail-modeladmin>=2.0,<3.0 qa: black qa: flake8 qa: mypy diff --git a/wagtail_parler/__init__.py b/wagtail_parler/__init__.py index 1f7e818..aa9a8d6 100644 --- a/wagtail_parler/__init__.py +++ b/wagtail_parler/__init__.py @@ -1,2 +1,2 @@ -__version__ = "0.1.1" +__version__ = "0.2.0" default_app_config = "wagtail_parler.apps.WagtailParlerConfig" diff --git a/wagtail_parler/compat.py b/wagtail_parler/compat.py deleted file mode 100644 index bc6f8c2..0000000 --- a/wagtail_parler/compat.py +++ /dev/null @@ -1,57 +0,0 @@ -# Future imports -from __future__ import annotations - -# Standard libs -from typing import Optional -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from typing import Union - from django.db.models import Model - from wagtail.admin.panels import Panel - -# Third Party -from wagtail import VERSION as WAGTAIL_VERSION -from wagtail.snippets.models import register_snippet as wagtail_register_snippet -from wagtail.snippets.views.snippets import SnippetViewSet - - -def register_snippet( - registerable: Union[Model, SnippetViewSet], viewset: Optional[SnippetViewSet] = None -) -> None: - if WAGTAIL_VERSION < (5, 0) and hasattr(registerable, "model"): - wagtail_register_snippet(registerable.model, viewset=registerable) - else: - wagtail_register_snippet(registerable, viewset) - - -class CompatibleSnippetViewSetMixin(SnippetViewSet): - pass - - -if WAGTAIL_VERSION < (5, 0): - # Third Party - from wagtail.admin.viewsets import viewsets - from wagtail.contrib.modeladmin.options import ModelAdmin - from wagtail.snippets.views.snippets import CreateView - from wagtail.snippets.views.snippets import EditView - - def get_edit_handler(self: CompatibleSnippetViewSetMixin) -> Panel: - return ModelAdmin.get_edit_handler(self) - - class CompatiblePanelViewMixin: - def get_panel(self: CreateView) -> Panel: - return next( - viewset.get_edit_handler() - for viewset in viewsets.viewsets - if viewset.model is self.model - ) - - CompatibleSnippetViewSetMixin.add_view_class = type( - "CreateView", (CompatiblePanelViewMixin, CreateView), {} - ) - CompatibleSnippetViewSetMixin.edit_view_class = type( - "EditView", (CompatiblePanelViewMixin, EditView), {} - ) - CompatibleSnippetViewSetMixin.get_edit_handler = get_edit_handler - CompatibleSnippetViewSetMixin.get_form_fields_exclude = lambda self: [] diff --git a/wagtail_parler/forms.py b/wagtail_parler/forms.py index 144c7c6..8756c00 100644 --- a/wagtail_parler/forms.py +++ b/wagtail_parler/forms.py @@ -5,20 +5,15 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: - from typing import Any - from typing import Dict - from typing import Generator - from typing import Optional - from typing import Set - from typing import Tuple + from typing import Any, Dict, Generator, Optional, Set, Tuple + from django.db.models import Model # Django imports from django.conf import settings from django.db import transaction from django.forms import Form -from django.forms.models import ModelForm -from django.forms.models import fields_for_model +from django.forms.models import ModelForm, fields_for_model class AutoParlerModelForm(Form): @@ -33,7 +28,7 @@ class AutoParlerModelForm(Form): def __init__(self, *args: Tuple, **kwargs: Dict) -> None: kwargs.setdefault("initial", {}) self._init_i18n_initials(kwargs.get("instance"), kwargs["initial"]) - self.for_user = kwargs.pop("for_user") + self.for_user = kwargs.pop("for_user", None) super().__init__(*args, **kwargs) # All fields of others locales than the default one must NOT be required for conf in settings.PARLER_LANGUAGES[None][1:]: diff --git a/wagtail_parler/handlers.py b/wagtail_parler/handlers.py index a000ff8..8f435f7 100644 --- a/wagtail_parler/handlers.py +++ b/wagtail_parler/handlers.py @@ -6,14 +6,10 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: - from typing import Dict - from typing import List - from typing import Optional - from typing import Set - from typing import Tuple + from typing import Dict, List, Optional, Set, Tuple + from wagtail.admin.panels import Panel - from wagtail.contrib.modeladmin.options import ModelAdmin - from wagtail.snippets.views.snippets import SnippetViewSet + from wagtail_modeladmin.options import ModelAdmin # Django imports from django.conf import settings @@ -22,15 +18,13 @@ from django.utils.translation import gettext_lazy as _ # Third Party -from wagtail.admin.panels import FieldPanel -from wagtail.admin.panels import ObjectList -from wagtail.admin.panels import TabbedInterface +from wagtail.admin.panels import FieldPanel, ObjectList, TabbedInterface +from wagtail.snippets.views.snippets import SnippetViewSet # wagtail / parler from wagtail_parler import settings as wp_settings # Local Apps -from .compat import CompatibleSnippetViewSetMixin from .forms import build_translations_form @@ -155,10 +149,26 @@ def get_edit_handler(self: ModelAdmin) -> TabbedInterface: ) ] displayed_fields = self._set_translations_handlers(handlers) + # currently (wagtail 6), `ModelViewSet.formfield_for_dbfield` returns + # `db_field.formfield(**kwargs)` instead of using the wagtail registry fields overrides + # as the WagtailAdminModelForm do via it's Meta.formfield_callback. + # So we don't want to use ModelViewSet.formfield_for_dbfield BUT if the user + # defines it's own MyViewSet.formfield_for_dbfield, we must use it (because he knows + # what he does and it's his responsability to use the wagtail registry fields overrides) + # sources: + # - https://github.com/wagtail/wagtail/blob/main/wagtail/admin/viewsets/model.py#L534 + # - https://github.com/wagtail/wagtail/blob/main/wagtail/admin/forms/models.py#L129 + main_form_meta_attrs = {} + formfield_for_dbfield = getattr(self, "formfield_for_dbfield", None) + if ( + formfield_for_dbfield + and formfield_for_dbfield.__func__.__qualname__ != "ModelViewSet.formfield_for_dbfield" + ): + main_form_meta_attrs["formfield_callback"] = self.formfield_for_dbfield base_form_class = build_translations_form( self.model, fields_for_model_kwargs={"fields": displayed_fields}, - base_form=getattr(self, "form", None), + base_form=getattr(self, "parler_base_form_class", None), ) return TabbedInterface(handlers, base_form_class=base_form_class) @@ -192,7 +202,7 @@ class FoodModelAdmin(ParlerModelAdminMixin, ModelAdmin): """ -class ParlerSnippetAdminMixin(ParlerAdminWagtailMixin, CompatibleSnippetViewSetMixin): +class ParlerSnippetAdminMixin(ParlerAdminWagtailMixin, SnippetViewSet): """ Mixin to use with SnippetViewSet to manage parler translations. diff --git a/wagtail_parler_tests/settings.py b/wagtail_parler_tests/settings.py index 52855ea..a8aa4c5 100644 --- a/wagtail_parler_tests/settings.py +++ b/wagtail_parler_tests/settings.py @@ -15,6 +15,7 @@ "wagtail_parler", "wagtail_parler_tests", "parler", + "wagtail_modeladmin", ] + [app for app in INSTALLED_APPS if not app.startswith("wagtail.test")] MIDDLEWARE = [m for m in MIDDLEWARE if not m.startswith("wagtail.test")] # remove test middleware diff --git a/wagtail_parler_tests/wagtail_hooks.py b/wagtail_parler_tests/wagtail_hooks.py index d47400b..9b9beb3 100644 --- a/wagtail_parler_tests/wagtail_hooks.py +++ b/wagtail_parler_tests/wagtail_hooks.py @@ -5,22 +5,24 @@ from django.utils.translation import gettext_lazy as _ # Third Party -from wagtail.admin.panels import FieldPanel -from wagtail.admin.panels import ObjectList -from wagtail.contrib.modeladmin.options import ModelAdmin -from wagtail.contrib.modeladmin.options import modeladmin_register +from wagtail.admin.panels import FieldPanel, ObjectList +from wagtail.snippets.models import register_snippet from wagtail.snippets.views.snippets import SnippetViewSet -from wagtail_parler.compat import register_snippet +from wagtail_modeladmin.options import ModelAdmin, modeladmin_register # wagtail / parler -from wagtail_parler.handlers import ParlerModelAdminMixin -from wagtail_parler.handlers import ParlerSnippetAdminMixin -from wagtail_parler.handlers import TranslationsList -from wagtail_parler_tests.models import Food -from wagtail_parler_tests.models import FoodWithEditHandler -from wagtail_parler_tests.models import FoodWithEmptyEditHandler -from wagtail_parler_tests.models import FoodWithPanelsInsideModel -from wagtail_parler_tests.models import FoodWithSpecificEditHandler +from wagtail_parler.handlers import ( + ParlerModelAdminMixin, + ParlerSnippetAdminMixin, + TranslationsList, +) +from wagtail_parler_tests.models import ( + Food, + FoodWithEditHandler, + FoodWithEmptyEditHandler, + FoodWithPanelsInsideModel, + FoodWithSpecificEditHandler, +) specific_edit_handler = ObjectList( children=[