diff --git a/README.md b/README.md index 2d017bc..261fa82 100644 --- a/README.md +++ b/README.md @@ -113,4 +113,43 @@ class MovieAdmin(GroupedActionAdminMixin, admin.ModelAdmin): # … ``` -The order of the groups is determined by the order of the individual actions: the first action for that group for each group determines how the groups are listed. \ No newline at end of file +The order of the groups is determined by the order of the individual actions: the first action for that group for each group determines how the groups are listed. + +## Simpler `SimpleListFilter`s + +Django allows to add extra filters on the Django admin, but even with a `SimpleListFilter`, they are cumbersome to make. We define a `ChoiceListFilter` which can be +used to make these based on a dictionary, and add a function `adminfilter_factory(..)` to define such classes in a more convenient way. + +Once can define such simple list filter by working with a dict like: + +```python3 +from django.db.models import Q +from django.db.models.functions import Now +from django_adminlink.admin import ChoiceListFilter + +class ArchiveStatusFilter(ChoiceListFilter): + title = 'Archive status' + parameter_name = 'archive_status' + choices = { + 'active': ('Active', ~Q(archived_at__lte=Now())), + 'archived': ('Archived', Q(archived_at__lte=Now())) + } +``` + +so a dictionary that maps the key of the filter on a 2-tuple with the verbose name as first item, and the Django filter (a `Q` object) as second one. Because it is a dictionary, it is also impossible to specify the same key twice. + +We can also define this with the `adminfilter_factory(..)` as follows: + + +```python3 +from django.db.models import Q +from django_adminlink.admin import adminfilter_factory + +ARCHIVE_OPTIONS = [ + ('active', 'Active', ~Q(archived_at__lte=Now())), + ('archived', 'Archived', Q(archived_at__lte=Now())) +] +ArchiveStatusFilter = adminfilter_factory('archive_status', ARCHIVE_OPTIONS) +``` + +here we can use the same dictionary structure, or any iterable of items with at least three items. \ No newline at end of file diff --git a/django_adminlink/admin.py b/django_adminlink/admin.py index 0eea8b6..57e1b4d 100644 --- a/django_adminlink/admin.py +++ b/django_adminlink/admin.py @@ -162,3 +162,31 @@ def get_action_choices(self, request, default_choices=models.BLANK_CHOICE_DASH): class GroupedActionAdmin(SingleItemActionMixin, admin.ModelAdmin): pass + + +class ChoiceListFilter(admin.SimpleListFilter): + choices = {} + + def lookups(self, request, model_admin): + return [(k, v) for k, (v, *__) in self.choices.items()] + + def queryset(self, request, queryset): + val = self.choices.get(self.value()) + if val and len(val) >= 2: + __, cond, *__ = val + return queryset.filter(cond) + return queryset + + +def adminfilter_factory(parameter_name, choices, verbose_name=None): + default_verbose_name = parameter_name.replace("_", " ") + if verbose_name is None: + verbose_name = default_verbose_name.capitalize() + class_name = default_verbose_name.title().replace(" ", "") + if not isinstance(choices, dict): + choices = {k: vs for k, *vs in choices} + return type( + f"{class_name}Filter", + (ChoiceListFilter,), + {"title": verbose_name, "parameter_name": parameter_name, "choices": choices}, + ) diff --git a/django_adminlink/tests.py b/django_adminlink/tests.py new file mode 100644 index 0000000..e69de29