diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 4bd22c51..33861695 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,6 +5,7 @@ Changelog Unreleased ========== * fix: Moved category list filter after site +* fix: Ordering category filter by name 1.6.0 (2022-04-29) ================== diff --git a/djangocms_alias/admin.py b/djangocms_alias/admin.py index 036ce77a..ba42c771 100644 --- a/djangocms_alias/admin.py +++ b/djangocms_alias/admin.py @@ -9,7 +9,7 @@ from .cms_config import AliasCMSConfig from .constants import USAGE_ALIAS_URL_NAME -from .filters import LanguageFilter, SiteFilter +from .filters import CategoryFilter, LanguageFilter, SiteFilter from .forms import AliasContentForm from .models import Alias, AliasContent, Category from .urls import urlpatterns @@ -28,7 +28,7 @@ alias_content_admin_classes = [admin.ModelAdmin] alias_content_admin_list_display = ('name', 'get_category',) -alias_content_admin_list_filter = (SiteFilter, LanguageFilter, 'alias__category', ) +alias_content_admin_list_filter = (SiteFilter, CategoryFilter, LanguageFilter,) djangocms_versioning_enabled = AliasCMSConfig.djangocms_versioning_enabled if djangocms_versioning_enabled: @@ -37,8 +37,7 @@ from .filters import UnpublishedFilter alias_content_admin_classes.insert(0, ExtendedVersionAdminMixin) alias_content_admin_list_display = ('name', 'get_category',) - alias_content_admin_list_filter = (SiteFilter, LanguageFilter, UnpublishedFilter) - alias_content_admin_list_filter = (SiteFilter, 'alias__category', LanguageFilter, UnpublishedFilter) + alias_content_admin_list_filter = (SiteFilter, CategoryFilter, LanguageFilter, UnpublishedFilter) @admin.register(Category) diff --git a/djangocms_alias/constants.py b/djangocms_alias/constants.py index 413b4f48..43884cf0 100644 --- a/djangocms_alias/constants.py +++ b/djangocms_alias/constants.py @@ -19,3 +19,4 @@ SITE_FILTER_URL_PARAM = "site" SITE_FILTER_NO_SITE_VALUE = "no_site" UNPUBLISHED_FILTER_URL_PARAM = "unpublished" +CATEGORY_FILTER_PARAM = "category" diff --git a/djangocms_alias/filters.py b/djangocms_alias/filters.py index 02975f0c..4d399d4d 100644 --- a/djangocms_alias/filters.py +++ b/djangocms_alias/filters.py @@ -1,4 +1,5 @@ from django.contrib import admin +from django.utils.encoding import smart_str from django.utils.translation import gettext_lazy as _ from cms.forms.utils import get_sites @@ -6,10 +7,12 @@ from .cms_config import AliasCMSConfig from .constants import ( + CATEGORY_FILTER_PARAM, LANGUAGE_FILTER_URL_PARAM, SITE_FILTER_NO_SITE_VALUE, SITE_FILTER_URL_PARAM, ) +from .models import Category djangocms_versioning_enabled = AliasCMSConfig.djangocms_versioning_enabled @@ -82,6 +85,38 @@ def choices(self, changelist): } +class CategoryFilter(admin.SimpleListFilter): + title = _("Category") + parameter_name = CATEGORY_FILTER_PARAM + + def lookups(self, request, model_admin): + qs = model_admin.get_queryset(request) + cat_id = qs.values_list('alias__category', flat=True).distinct() + # Ensure the category is ordered by the name alphabetically by default + cat = Category.objects.filter(pk__in=cat_id).order_by('translations__name') + for obj in cat: + yield str(obj.pk), smart_str(obj) + + def queryset(self, request, queryset): + if self.value(): + return queryset.filter(alias__category=self.value()).distinct() + + def choices(self, changelist): + yield { + "selected": self.value() is None, + "query_string": changelist.get_query_string(remove=[self.parameter_name]), + "display": _("All"), + } + for lookup, title in self.lookup_choices: + yield { + "selected": self.value() == str(lookup), + "query_string": changelist.get_query_string( + {self.parameter_name: lookup} + ), + "display": title, + } + + if djangocms_versioning_enabled: from djangocms_versioning.constants import UNPUBLISHED diff --git a/tests/test_admin_filters.py b/tests/test_admin_filters.py index 379a90b2..6dfca0de 100644 --- a/tests/test_admin_filters.py +++ b/tests/test_admin_filters.py @@ -1,5 +1,6 @@ from unittest import skipUnless +from django.contrib import admin from django.contrib.sites.models import Site from cms.utils import get_current_site @@ -9,6 +10,7 @@ SITE_FILTER_URL_PARAM, UNPUBLISHED_FILTER_URL_PARAM, ) +from djangocms_alias.filters import CategoryFilter from djangocms_alias.models import Alias as AliasModel, AliasContent, Category from djangocms_alias.utils import is_versioning_enabled @@ -252,9 +254,9 @@ def test_category_filter_no_verisoning(self): with self.login_user_context(self.superuser): response_default = self.client.get(base_url) # category one should have a result - category_one_filter_response = self.client.get(f"{base_url}?alias__category__id__exact={category_one.id}") + category_one_filter_response = self.client.get(f"{base_url}?category={category_one.id}") # category two should have a result - category_two_filter_response = self.client.get(f"{base_url}?alias__category__id__exact={category_two.id}") + category_two_filter_response = self.client.get(f"{base_url}?category={category_two.id}") # By default all alias contents are shown self.assertEqual( @@ -310,9 +312,9 @@ def test_category_filter_with_verisoning(self): with self.login_user_context(self.superuser): response_default = self.client.get(base_url) # category one should have a result - category_one_filter_response = self.client.get(f"{base_url}?alias__category__id__exact={category_one.id}") + category_one_filter_response = self.client.get(f"{base_url}?category={category_one.id}") # categopry two should have a result - category_two_filter_response = self.client.get(f"{base_url}?alias__category__id__exact={category_two.id}") + category_two_filter_response = self.client.get(f"{base_url}?category={category_two.id}") # By default all alias contents are shown self.assertEqual( @@ -332,3 +334,76 @@ def test_category_filter_with_verisoning(self): set(category_two_filter_response.context["cl"].queryset), set([expected_category_two_content]) ) + + def test_category_filter_lookups_ordered_alphabetical(self): + """ + Category filter lookup choices should be ordered in alphabetical order + """ + category_one = Category.objects.create(name='b - category') + alias_one = AliasModel.objects.create( + category=category_one, + position=0, + ) + AliasContent.objects.create( + alias=alias_one, + name="EN Alias Content one", + language="en", + ) + category_two = Category.objects.create(name='a - category') + alias_two = AliasModel.objects.create( + category=category_two, + position=1, + ) + AliasContent.objects.create( + alias=alias_two, + name="EN Alias Content two", + language="en", + ) + + version_admin = admin.site._registry[AliasContent] + category_filter = CategoryFilter(None, {"category": ""}, AliasContent, version_admin) + # Get the first choice in the filter lookup object + first_lookup_value = category_filter.lookup_choices[0][1] + # Lookup value should match the category name linked to alias content + self.assertEqual( + first_lookup_value, category_two.name + ) + self.assertNotEqual( + first_lookup_value, category_one.name + ) + + def test_category_filter_lookup_should_only_show_aliases_linked_to_content(self): + """ + Category not linked to content should not be listed in the category filter lookups + """ + category_one = Category.objects.create(name='b - category') + alias_one = AliasModel.objects.create( + category=category_one, + position=0, + ) + AliasContent.objects.create( + alias=alias_one, + name="EN Alias Content one", + language="en", + ) + category_two = Category.objects.create(name='a - category') + AliasModel.objects.create( + category=category_two, + position=1, + ) + + version_admin = admin.site._registry[AliasContent] + category_filter = CategoryFilter(None, {"category": ""}, AliasContent, version_admin) + + # Get the first choice in the filter lookup object + first_lookup_value = category_filter.lookup_choices[0][1] + # Lookup choices should only display the category linked to content + self.assertEqual(len(category_filter.lookup_choices), 1) + # Lookup value should match the category name linked to alias content + self.assertEqual( + first_lookup_value, category_one.name + ) + # Category not linked to alias content should not be listed in the choices + self.assertNotEqual( + first_lookup_value, category_two.name + )