多选择django管理过滤器可能吗?

fv2wmkja  于 2023-08-08  发布在  Go
关注(0)|答案(5)|浏览(93)

假设有一个名为'Cities'的模型,并且在管理端有一个过滤器'city_name'来根据城市名称过滤模型。默认情况下,django只允许从django admin过滤器中选择一个城市名称。但是我需要在django管理过滤器中选择多个城市名称。我该怎么做呢??

km0tfn4u

km0tfn4u1#

您可以使用AllValuesFieldListFilter中内置的稍做修改的版本:

from django.contrib import admin
from django.contrib.admin.utils import reverse_field_path
from django.utils.translation import gettext_lazy as _

class MultiSelectFieldListFilter(admin.FieldListFilter):
    def __init__(self, field, request, params, model, model_admin, field_path):
        self.lookup_kwarg = field_path + '__in'
        self.lookup_kwarg_isnull = field_path + '__isnull'

        super().__init__(field, request, params, model, model_admin, field_path)

        self.lookup_val = self.used_parameters.get(self.lookup_kwarg, [])
        if len(self.lookup_val) == 1 and self.lookup_val[0] == '':
            self.lookup_val = []
        self.lookup_val_isnull = self.used_parameters.get(self.lookup_kwarg_isnull)

        self.empty_value_display = model_admin.get_empty_value_display()
        parent_model, reverse_path = reverse_field_path(model, field_path)
        # Obey parent ModelAdmin queryset when deciding which options to show
        if model == parent_model:
            queryset = model_admin.get_queryset(request)
        else:
            queryset = parent_model._default_manager.all()
        self.lookup_choices = queryset.distinct().order_by(field.name).values_list(field.name, flat=True)

    def expected_parameters(self):
        return [self.lookup_kwarg, self.lookup_kwarg_isnull]

    def choices(self, changelist):
        yield {
            'selected': not self.lookup_val and self.lookup_val_isnull is None,
            'query_string': changelist.get_query_string(remove=[self.lookup_kwarg, self.lookup_kwarg_isnull]),
            'display': _('All'),
        }
        include_none = False
        for val in self.lookup_choices:
            if val is None:
                include_none = True
                continue
            val = str(val)

            if val in self.lookup_val:
                values = [v for v in self.lookup_val if v != val]
            else:
                values = self.lookup_val + [ val ]

            if values:
                yield {
                    'selected': val in self.lookup_val,
                    'query_string': changelist.get_query_string({self.lookup_kwarg: ','.join(values)}, [self.lookup_kwarg_isnull]),
                    'display': val,
                }
            else:
                yield {
                    'selected': val in self.lookup_val,
                    'query_string': changelist.get_query_string(remove=[self.lookup_kwarg]),
                    'display': val,
                }

        if include_none:
            yield {
                'selected': bool(self.lookup_val_isnull),
                'query_string': changelist.get_query_string({self.lookup_kwarg_isnull: 'True'}, [self.lookup_kwarg]),
                'display': self.empty_value_display,
            }

字符串
值中的转义逗号也应该实现,但我不需要它。
(使用Django 2.2.5测试)

4uqofj5v

4uqofj5v3#

对于那些更愿意使用外部库的人来说,这些库似乎在撰写本文时得到了维护(最近6个月内的发布):
django-more-admin-filters:添加了几个过滤器类,包括多选和下拉过滤器
django-advanced-filters:添加了一个高级过滤器mixin,它可以显示一个模式,以构建由和/或组合的多个条件的查询
与问题无关,但也是一个很好的发现:
django-admin-autocomplete-list-filter:使自动完成列表过滤器成为可能

9o685dep

9o685dep4#

你可以通过继承django.contrib.admin.SimpleListFilter来使用Django自定义list_filter
下面是一个简单的例子:

from django.contrib import admin

class MultiSelectFilter(admin.SimpleListFilter):
    # Filter title
    title = 'Cities'

    # model field
    parameter_name = 'city_name'

    def lookups(self, request, model_admin):
        # you can modify this part, this is less DRY approach.
        # P.S. assuming city_name is lowercase CharField
        return (
            ('city1,city2,city3', 'City1 or City2 or City3'),
            ('city4,city5,city6', 'City3 or City4 or City6'),
        )

    def queryset(self, request, queryset):

        if self.value() in ('city1,city2,city3', 'city4,city5,city6'):
            # filter if a choice selected
            return queryset.filter(city_name__in=self.value().split(','))
        # default for no filtering
        return queryset

字符串
您在此处有文档的引用

lsmepo6l

lsmepo6l5#

也可以使用django-filter包实现多个选择过滤器:

class PersonFilterSet(django_filters.FilterSet):
    city = django_filters.ModelMultipleChoiceFilter(
        queryset=City.objects.all(),
        label='Cities')

    class Meta:
        model = Person
        fields = ('city',)

@admin.register(Person)
class PersonAdmin(ModelAdmin):
    list_filter = form_filter_factory(PersonFilterSet),

字符串
这个解决方案更好,因为许多Django项目已经使用了django-filter包,所以只需要管理过滤器就不需要额外的依赖。

相关问题