diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index f541d70..a1c808c 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -12,7 +12,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v2 with: - python-version: 3.8 + python-version: '3.10' - name: Install flake8 run: pip install --upgrade flake8 - name: Run flake8 @@ -29,7 +29,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v2 with: - python-version: 3.8 + python-version: '3.10' - run: python -m pip install isort - name: isort uses: liskin/gh-problem-matcher-wrap@v1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e48e91a..8ec9ce4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,9 +10,10 @@ jobs: strategy: fail-fast: false matrix: - python-version: [ 3.8, 3.9, ] # latest release minus two + python-version: [ 3.8, 3.9, '3.10' ] # latest release minus two requirements-file: [ dj32_cms40.txt, + dj42_cms40.txt, ] os: [ ubuntu-20.04, @@ -42,9 +43,10 @@ jobs: strategy: fail-fast: false matrix: - python-version: [ 3.8, 3.9, ] # latest release minus two + python-version: [ 3.8, 3.9, '3.10' ] # latest release minus two requirements-file: [ dj32_cms40.txt, + dj42_cms40.txt, ] steps: diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 81ed1fe..ab058ae 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,10 @@ Changelog Unreleased ========== +* Python 3.10 support added +* Django 4.2 support added +* Django < 3.2 support removed + 1.1.0 (2022-02-22) ================== diff --git a/djangocms_url_manager/admin.py b/djangocms_url_manager/admin.py index b55200b..d0aa17b 100644 --- a/djangocms_url_manager/admin.py +++ b/djangocms_url_manager/admin.py @@ -44,6 +44,9 @@ class UrlAdmin(*url_admin_classes): def get_urls(self): return urlpatterns + super().get_urls() + @admin.display( + description="URL" + ) def get_model_url(self, obj): return obj.get_url(obj.site) @@ -62,8 +65,6 @@ def get_search_results(self, request, queryset, search_term): return queryset, use_distinct - get_model_url.short_description = "URL" - def change_view(self, request, object_id, form_url='', extra_context=None): extra_context = extra_context or {} # Provide additional context to the changeform diff --git a/djangocms_url_manager/apps.py b/djangocms_url_manager/apps.py index 68ab0bc..a2cac19 100644 --- a/djangocms_url_manager/apps.py +++ b/djangocms_url_manager/apps.py @@ -1,5 +1,4 @@ from django.apps import AppConfig -from django.conf import settings from django.utils.translation import gettext_lazy as _ @@ -7,13 +6,3 @@ class UrlManagerConfig(AppConfig): name = "djangocms_url_manager" verbose_name = _("django CMS URL Manager") url_manager_supported_models = {} - - def ready(self): - from .compat import CMS_36 - - if CMS_36: - from .utils import parse_settings - - self.url_manager_supported_models = parse_settings( - settings, "URL_MANAGER_SUPPORTED_MODELS" - ) diff --git a/djangocms_url_manager/compat.py b/djangocms_url_manager/compat.py index e261697..8375da7 100644 --- a/djangocms_url_manager/compat.py +++ b/djangocms_url_manager/compat.py @@ -1,11 +1,9 @@ -from distutils.version import LooseVersion +import django -import cms +from packaging.version import Version -CMS_VERSION = cms.__version__ - -CMS_36 = LooseVersion(CMS_VERSION) < LooseVersion("3.7") +DJANGO_4_2 = Version(django.get_version()) >= Version('4.2') and Version(django.get_version()) < Version('4.3') def get_page_placeholders(page, language=None): diff --git a/djangocms_url_manager/urls.py b/djangocms_url_manager/urls.py index 1b420df..3b76fc3 100644 --- a/djangocms_url_manager/urls.py +++ b/djangocms_url_manager/urls.py @@ -1,15 +1,15 @@ -from django.urls import re_path +from django.urls import path from . import constants, views urlpatterns = [ - re_path( - r"^select2/$", + path( + "select2/", views.ContentTypeObjectSelect2View.as_view(), name=constants.SELECT2_CONTENT_TYPE_OBJECT_URL_NAME, ), - re_path( - r"^select2/urls/$", views.UrlSelect2View.as_view(), name=constants.SELECT2_URLS + path( + "select2/urls/", views.UrlSelect2View.as_view(), name=constants.SELECT2_URLS ), ] diff --git a/djangocms_url_manager/utils.py b/djangocms_url_manager/utils.py index 4cadd9c..1c7440f 100644 --- a/djangocms_url_manager/utils.py +++ b/djangocms_url_manager/utils.py @@ -10,15 +10,13 @@ from cms.models import PageContent -from djangocms_url_manager.compat import CMS_36 - def parse_settings(config, attr_name): url_manager_supported_models = OrderedDict() if not hasattr(config, attr_name): raise ImproperlyConfigured( "{} must be defined in your {}".format( - attr_name, "settings" if CMS_36 else "cms_config" + attr_name, "cms_config" ) ) models = getattr(config, attr_name) diff --git a/setup.py b/setup.py index d899303..7696231 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ INSTALL_REQUIREMENTS = [ - "Django>=2.2,<4.0", + "Django>=3.2,<5.0", "django-cms", "djangocms-attributes-field", ] diff --git a/tests/__init__.py b/tests/__init__.py index e69de29..4771bc8 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -0,0 +1,7 @@ +from djangocms_url_manager.compat import DJANGO_4_2 + + +if not DJANGO_4_2: # TODO: remove when dropping support for Django < 4.2 + from django.test.testcases import TransactionTestCase + + TransactionTestCase.assertQuerySetEqual = TransactionTestCase.assertQuerysetEqual diff --git a/tests/base.py b/tests/base.py index 6b21e36..00d718d 100644 --- a/tests/base.py +++ b/tests/base.py @@ -9,7 +9,7 @@ from cms.utils.urlutils import admin_reverse from djangocms_url_manager.admin import UrlAdmin -from djangocms_url_manager.compat import CMS_36, get_page_placeholders +from djangocms_url_manager.compat import get_page_placeholders from djangocms_url_manager.constants import ( SELECT2_CONTENT_TYPE_OBJECT_URL_NAME, SELECT2_URLS, @@ -148,9 +148,6 @@ def _create_page(self, title, language=None, site=None, published=True, **kwargs if self.is_versioning_enabled() and not kwargs.get("created_by"): kwargs["created_by"] = self.superuser - if CMS_36 and published: - kwargs["published"] = True - page = create_page( title=title, language=language, diff --git a/tests/requirements/dj32_cms40.txt b/tests/requirements/dj32_cms40.txt index e5214ce..8b26855 100644 --- a/tests/requirements/dj32_cms40.txt +++ b/tests/requirements/dj32_cms40.txt @@ -1,3 +1,8 @@ -r requirements_base.txt Django>=3.2,<4.0 + +# Unreleased django 3.2 & django-cms 4.0 compatible packages +https://github.com/django-cms/django-cms/tarball/release/4.0.1.x#egg=django-cms +https://github.com/django-cms/djangocms-versioning/tarball/1.2.2#egg=djangocms-versioning +https://github.com/django-cms/djangocms-moderation/tarball/2.1.5#egg=djangocms-moderation diff --git a/tests/requirements/dj42_cms40.txt b/tests/requirements/dj42_cms40.txt new file mode 100644 index 0000000..98a6fe1 --- /dev/null +++ b/tests/requirements/dj42_cms40.txt @@ -0,0 +1,8 @@ +-r requirements_base.txt + +Django>=4.2,<5.0 + +# Unreleased django 4.2 compatible packages +https://github.com/django-cms/django-cms/tarball/release/4.0.1.x#egg=django-cms +https://github.com/joshyu/djangocms-versioning/tarball/feat/django-42-compatible#egg=djangocms-versioning +https://github.com/FidelityInternational/djangocms-moderation/tarball/feature/django-42-compat#egg=djangocms-moderation \ No newline at end of file diff --git a/tests/requirements/requirements_base.txt b/tests/requirements/requirements_base.txt index d6a0eb6..8415707 100644 --- a/tests/requirements/requirements_base.txt +++ b/tests/requirements/requirements_base.txt @@ -1,7 +1,7 @@ coverage django-app-helper django-classy-tags -django-formtools==2.2 +django-formtools>=2.4.1 django-sekizai django-treebeard djangocms-admin-style @@ -9,9 +9,4 @@ djangocms-attributes-field factory-boy flake8 isort -pyflakes>=2.1.1 - -# Unreleased django-cms 4.0 compatible packages -https://github.com/django-cms/django-cms/tarball/develop-4#egg=django-cms -https://github.com/django-cms/djangocms-versioning/tarball/master#egg=djangocms-versioning -https://github.com/django-cms/djangocms-moderation/tarball/master#egg=djangocms-moderation +pyflakes diff --git a/tests/test_cms_config.py b/tests/test_cms_config.py index 5eecf7f..548d50f 100644 --- a/tests/test_cms_config.py +++ b/tests/test_cms_config.py @@ -1,5 +1,4 @@ from importlib import reload -from unittest import skipIf from unittest.mock import Mock from django.conf import settings @@ -9,7 +8,6 @@ from cms.models import Page from cms.test_utils.testcases import CMSTestCase -from djangocms_url_manager.compat import CMS_36 from djangocms_url_manager.test_utils.polls.models import Poll, PollContent from djangocms_url_manager.test_utils.polls.utils import ( get_all_poll_content_objects, @@ -22,7 +20,6 @@ ) -@skipIf(CMS_36, "Test relevant only for CMS>=4.0") class UrlManagerCMSExtensionTestCase(CMSTestCase): def test_missing_cms_config_url_manager_supported_models_attribute(self): """Tests, if the url_manager_supported_models attribute has not been specified, @@ -145,7 +142,6 @@ def test_url_manager_search_helpers(self): ) -@skipIf(CMS_36, "Test relevant only for CMS>=4.0") class NavigationSettingTestCase(TestCase): def tearDownClass(): diff --git a/tests/test_settings.py b/tests/test_settings.py index 30edb0d..87b91f0 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -1,4 +1,4 @@ -from unittest import skipUnless +from unittest import skip from django.apps import apps from django.conf import settings @@ -7,13 +7,12 @@ from cms.test_utils.testcases import CMSTestCase -from djangocms_url_manager.compat import CMS_36 from djangocms_url_manager.test_utils.polls.models import PollContent from djangocms_url_manager.test_utils.polls.utils import get_all_poll_content_objects from djangocms_url_manager.utils import supported_models -@skipUnless(CMS_36, "Test relevant only for CMS<4.0") +@skip("Skip test, as dropped support for python < 3.7.") class CMSSettingsUnitTestCase(CMSTestCase): def tearDown(self): supported_models.cache_clear() diff --git a/tests/test_utils.py b/tests/test_utils.py index df5f37d..d284f63 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,54 +1,20 @@ -from unittest import skipIf, skipUnless - -from django.apps import apps -from django.test import override_settings - from cms.models import Page, Placeholder -from djangocms_url_manager.compat import CMS_36 from djangocms_url_manager.test_utils.polls.models import PollContent -from djangocms_url_manager.test_utils.polls.utils import ( - get_all_poll_content_objects, - get_published_pages_objects, -) +from djangocms_url_manager.test_utils.polls.utils import get_all_poll_content_objects from djangocms_url_manager.utils import is_model_supported, supported_models from .base import BaseUrlTestCase class UtilsTestCase(BaseUrlTestCase): - @skipUnless(CMS_36, "Test relevant only for CMS<4.0") - def test_supported_models_for_cms36(self): - apps.get_app_config("djangocms_url_manager").ready() - self.assertDictEqual( - supported_models(), - { - Page: get_published_pages_objects, - PollContent: get_all_poll_content_objects, - }, - ) - @skipIf(CMS_36, "Test relevant only for CMS>=4.0") - def test_supported_models_for_cms40(self): + def test_supported_models(self): self.assertDictEqual( supported_models(), {Page: None, PollContent: get_all_poll_content_objects} ) - @skipUnless(CMS_36, "Test relevant only for CMS<4.0") - @override_settings( - URL_MANAGER_SUPPORTED_MODELS=[ - ("cms.Page", get_published_pages_objects), - ("polls.PollContent", get_all_poll_content_objects), - ] - ) - def test_is_model_available_method_for_cms36(self): - apps.get_app_config("djangocms_url_manager").ready() - self.assertTrue(is_model_supported(PollContent)) - self.assertTrue(is_model_supported(Page)) - self.assertFalse(is_model_supported(Placeholder)) - - @skipIf(CMS_36, "Test relevant only for CMS>=4.0") - def test_is_model_available_method_for_cms40(self): + def test_is_model_available_method(self): self.assertTrue(is_model_supported(PollContent)) self.assertTrue(is_model_supported(Page)) self.assertFalse(is_model_supported(Placeholder)) diff --git a/tests/test_versioning_integration.py b/tests/test_versioning_integration.py index a7d158f..ed458b2 100644 --- a/tests/test_versioning_integration.py +++ b/tests/test_versioning_integration.py @@ -311,7 +311,7 @@ def test_compare_view(self): # Now do some real testing, we can't check what was actually rendered because this is # done by a separate call by AJAX, the best that we can do is check the query matches self.assertIn("version_list", context) - self.assertQuerysetEqual( + self.assertQuerySetEqual( context["version_list"], [published_url_content.pk, draft_url_content.pk], transform=lambda o: o.pk, diff --git a/tests/test_views.py b/tests/test_views.py index 6299f3c..ee756f9 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -1,11 +1,10 @@ -from unittest import skipIf, skipUnless +from unittest import skipUnless from django.contrib.contenttypes.models import ContentType -from cms.models import Page, User +from cms.models import User from cms.utils.urlutils import admin_reverse -from djangocms_url_manager.compat import CMS_36 from djangocms_url_manager.constants import SELECT2_URLS from djangocms_url_manager.test_utils.factories import UrlWithVersionFactory from djangocms_url_manager.utils import is_versioning_enabled @@ -26,26 +25,7 @@ def test_select2_view_no_permission(self): response = self.client.get(self.select2_endpoint) self.assertEqual(response.status_code, 403) - @skipUnless(CMS_36, "Test relevant only for CMS<4.0") - def test_return_page_in_select2_view_for_cms36(self): - with self.login_user_context(self.superuser): - response = self.client.get( - self.select2_endpoint, - data={"content_id": self.page_contenttype_id, "site": self.site2.pk}, - ) - self.assertEqual(response.status_code, 200) - self.assertEqual( - [p["id"] for p in response.json()["results"]], - [ - Page.objects.published(self.site2.pk) - .filter(publisher_is_draft=False) - .last() - .pk - ], - ) - - @skipIf(CMS_36, "Test relevant only for CMS>=4.0") - def test_return_page_in_select2_view_for_cms40(self): + def test_return_page_in_select2_view(self): with self.login_user_context(self.superuser): response = self.client.get( self.select2_endpoint, data={"content_id": self.page_contenttype_id} @@ -55,11 +35,8 @@ def test_return_page_in_select2_view_for_cms40(self): [p["id"] for p in response.json()["results"]], [self.page.pk, self.page2.pk] ) - @skipIf(CMS_36, "Test relevant only for CMS>=4.0") - @skipUnless( - BaseUrlTestCase.is_versioning_enabled(), "Test only relevant for versioning" - ) - def test_return_page_in_select2_view_with_versioning_and_cms40(self): + @skipUnless(BaseUrlTestCase.is_versioning_enabled(), "Test only relevant for versioning") + def test_return_page_in_select2_view_with_versioning(self): with self.login_user_context(self.superuser): response = self.client.get( self.select2_endpoint, data={"content_id": self.page_contenttype_id} @@ -120,22 +97,7 @@ def test_raise_error_when_return_unregistered_user_model_in_select2_view(self): data={"content_id": ContentType.objects.get_for_model(User).id}, ) - @skipUnless(CMS_36, "Test relevant only for CMS<4.0") - def test_select2_view_set_limit_for_cms36(self): - self._create_page(title="test 3", language=self.language) - with self.login_user_context(self.superuser): - response = self.client.get( - self.select2_endpoint, - data={"content_id": self.page_contenttype_id, "limit": 1}, - ) - - content = response.json() - self.assertEqual(response.status_code, 200) - self.assertTrue(content["more"]) - self.assertEqual(len(content["results"]), 1) - - @skipIf(CMS_36, "Test relevant only for CMS>=4.0") - def test_select2_view_set_limit_for_cms40(self): + def test_select2_view_set_limit(self): self._create_page(title="test 3", language=self.language) with self.login_user_context(self.superuser): response = self.client.get( @@ -167,26 +129,7 @@ def test_select2_view_text_poll_content_repr(self): self.assertEqual(response.status_code, 200) self.assertEqual(response.json()["results"][0]["text"], str(self.poll_content)) - @skipUnless(CMS_36, "Test relevant only for CMS<4.0") - def test_select2_view_site_for_cms36(self): - with self.login_user_context(self.superuser): - response = self.client.get( - self.select2_endpoint, - data={"content_id": self.page_contenttype_id, "site": self.site2.pk}, - ) - self.assertEqual(response.status_code, 200) - self.assertEqual( - [a["id"] for a in response.json()["results"]], - [ - Page.objects.published(self.site2.pk) - .filter(publisher_is_draft=False) - .last() - .pk - ], - ) - - @skipIf(CMS_36, "Test relevant only for CMS>=4.0") - def test_select2_view_site_for_cms40(self): + def test_select2_view_site(self): with self.login_user_context(self.superuser): response = self.client.get( self.select2_endpoint, @@ -195,28 +138,7 @@ def test_select2_view_site_for_cms40(self): self.assertEqual(response.status_code, 200) self.assertEqual([a["id"] for a in response.json()["results"]], [self.page2.pk]) - @skipUnless(CMS_36, "Test relevant only for CMS<4.0") - def test_select2_page_view_pk_for_cms36(self): - page = ( - Page.objects.published(self.site2.pk) - .filter(publisher_is_draft=False) - .last() - .pk - ) - with self.login_user_context(self.superuser): - response = self.client.get( - self.select2_endpoint, - data={ - "content_id": self.page_contenttype_id, - "site": self.site2.pk, - "pk": page, - }, - ) - self.assertEqual(response.status_code, 200) - self.assertEqual([a["id"] for a in response.json()["results"]], [page]) - - @skipIf(CMS_36, "Test relevant only for CMS>=4.0") - def test_select2_page_view_pk_for_cms40(self): + def test_select2_page_view_pk(self): with self.login_user_context(self.superuser): response = self.client.get( self.select2_endpoint, diff --git a/tox.ini b/tox.ini index 97fcfa4..f70933e 100644 --- a/tox.ini +++ b/tox.ini @@ -2,7 +2,7 @@ envlist = flake8 isort - py{38,39}-dj{32}-sqlite-cms40-{default,versioning} + py{38,39,'3.10'}-dj{32,42}-sqlite-cms40-{default,versioning} skip_missing_interpreters=True @@ -13,12 +13,14 @@ deps = -r {toxinidir}/tests/requirements/requirements_base.txt dj32: -r {toxinidir}/tests/requirements/django-3_2.txt + dj42: -r {toxinidir}/tests/requirements/dj42_cms40.txt cms40: https://github.com/divio/django-cms/archive/release/4.0.x.zip basepython = py38: python3.8 py39: python3.9 + py310: python3.10 commands = {envpython} --version @@ -28,8 +30,8 @@ commands = [testenv:flake8] commands = flake8 -basepython = python3.9 +basepython = python3.10 [testenv:isort] commands = isort --extra-builtin mock --recursive --check-only --diff {toxinidir} -basepython = python3.9 +basepython = python3.10