【发布时间】:2016-09-30 11:07:05
【问题描述】:
假设如果有一个名为“Cities”的模型,并且在管理员端有一个过滤器“city_name”来根据城市名称过滤模型。 默认情况下,django 只允许从 django 管理过滤器中选择一个城市名称。 但我需要在 django 管理过滤器中选择多个城市名称。我该怎么做。??
【问题讨论】:
标签: django django-models
假设如果有一个名为“Cities”的模型,并且在管理员端有一个过滤器“city_name”来根据城市名称过滤模型。 默认情况下,django 只允许从 django 管理过滤器中选择一个城市名称。 但我需要在 django 管理过滤器中选择多个城市名称。我该怎么做。??
【问题讨论】:
标签: django django-models
对于那些宁愿使用外部库的人,这些似乎在撰写本文时得到维护(在过去 6 个月内发布):
django-more-admin-filters:添加了几个过滤器类,包括。多选和下拉过滤器
django-advanced-filters:添加了一个高级过滤器 mixin,它可以显示一个模式来构建查询,其中包含多个由和/或组合的条件
与问题无关,但也是一个不错的发现:
django-admin-autocomplete-list-filter:使自动完成列表过滤成为可能
【讨论】:
您可以使用内置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 测试)
【讨论】:
您可以使用这个新工具django-admin-multiple-choice-list-filter。
【讨论】:
你可以通过继承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
您有参考文档here
【讨论】: