Python django管理员列表“U过滤器”;或;条件
如果这个问题以前有人回答过,我很抱歉,但我在谷歌上搜索了很多次都没有成功 我知道如何在管理视图中创建自定义的Python django管理员列表“U过滤器”;或;条件,python,django,filter,django-admin,django-admin-filters,Python,Django,Filter,Django Admin,Django Admin Filters,如果这个问题以前有人回答过,我很抱歉,但我在谷歌上搜索了很多次都没有成功 我知道如何在管理视图中创建自定义的列表过滤器s(例如,子类化SimpleFilter) 我真正想要的是(在管理列表视图上)一种“检查”不同过滤器的方法,这些过滤器将它们组合在一个或公式中 例如,假设您有: # models.py class Foo(models.Model): foobar = ... foofie = ... ... # admin.py class FooAdmin(admin.Mo
列表过滤器
s(例如,子类化SimpleFilter
)
我真正想要的是(在管理列表视图上)一种“检查”不同过滤器的方法,这些过滤器将它们组合在一个或公式中
例如,假设您有:
# models.py
class Foo(models.Model):
foobar = ...
foofie = ...
...
# admin.py
class FooAdmin(admin.ModelAdmin):
list_filter = ( "foobar", "foofie" )
...
在由FooAdmin
生成的管理列表视图中,我可以选择通过foobar
或foofie
过滤记录。有没有办法通过以下公式过滤它们:foobar=X或foofie=Y
,其中X
和Y
是foobar
和foofie
可以假定的两个值
有可能吗
我知道在django管理视图中并非一切都是可能的,但这似乎是一个非常常见的请求,我想知道我是否错过了理解或阅读某些内容
此外,还欢迎使用第三方应用程序。谢谢:)首先,我试图解释django管理过滤器的工作原理。当您想在管理页面中筛选查询集时,django会查找所有已注册的筛选器。如果您为filter django filter queryset设置值,请使用此值。如果您设置了多个筛选器django筛选器两次筛选您的查询集,这等于queryset=queryset.filter(param1=1).filter(param2=2)或在SQL中:选择。。。其中,param1=1和param2=2。这是因为你不能用标准的django过滤器。但您可以这样编写自己的过滤器:
from django.contrib.admin import SimpleListFilter
from django.db.models import Q
from functools import reduce
import operator
from django.core.exceptions import FieldError
class ORListFilter(SimpleListFilter):
title = ''
parameter_name = ''
search_field = ('',)
def queryset(self, request, queryset):
filters = request.GET.copy()
try: #for search
search_field_value = filters.pop('q')[0]
query_params = [Q((key, search_field_value)) for key in self.search_field]
try:
queryset = queryset.filter(reduce(operator.or_, query_params))
except FieldError:
pass
except KeyError:
pass
try:
query_params = [Q((key, value)) for key, value in filters.dict().items()]
queryset = queryset.filter(reduce(operator.or_, query_params))
except TypeError:
pass
return queryset
def lookups(self, request, model_admin):
qs = model_admin.get_queryset(request)
parameters = qs.all().values(self.parameter_name).distinct()
for parameter in parameters:
value = dict(parameter).pop(self.parameter_name, None)
if value:
yield (value, value)
else:
yield (None, 'NULL')
class Field1Filter(ORListFilter):
title = 'title'
parameter_name = 'field1'
search_field = ('search1', 'search2')
class Field2Filter(ORListFilter):
title = 'title'
parameter_name = 'field2'
search_field = ('search1', 'search2')
并在管理员中注册:
search_fields = ('search1', 'search2')
list_filter = (Field1Filter, Field2Filter)
它不适用于标准django的过滤器,列表过滤器中的所有值都必须继承自ORListFilter类。此外,它不适用于日期时间过滤器,但您可以添加此功能。想出了一个解决方案:
import operator
from functools import reduce
from django.contrib.admin import ListFilter, FieldListFilter
from django.db.models import Q
from django.contrib.admin.utils import (
get_fields_from_path, lookup_needs_distinct, prepare_lookup_value,
)
from django.http import QueryDict
class OrListFilter(ListFilter):
parameter_prefix = None
fields = None
def __init__(self, request, params, model, model_admin):
super(OrListFilter, self).__init__(
request, params, model, model_admin)
if self.parameter_prefix is None:
raise ImproperlyConfigured(
"The list filter '%s' does not specify "
"a 'parameter_prefix'." % self.__class__.__name__)
self.model_admin = model_admin
self.model = model
self.request = request
self.filter_specs = self.get_filters(request, {}, prefix=self.parameter_prefix+'-')
for p in self.expected_parameters():
if p in params:
value = params.pop(p)
field = p.split('-')[1]
self.used_parameters[field] = prepare_lookup_value(field, value)
def has_output(self):
return True
# see https://github.com/django/django/blob/1.8.5/django/contrib/admin/views/main.py#L104
def get_filters(self, request, params, prefix=''):
filter_specs = []
for field_path in self.fields:
field = get_fields_from_path(self.model, field_path)[-1]
field_list_filter_class = FieldListFilter.create
spec = field_list_filter_class(field, request, params,
self.model, self.model_admin, field_path=prefix + field_path)
# Check if we need to use distinct()
# use_distinct = (use_distinct or
# lookup_needs_distinct(self.lookup_opts,
# field_path))
filter_specs.append(spec)
return filter_specs
def expected_parameters(self):
parameters = []
for spec in self.filter_specs:
parameters += spec.expected_parameters()
return parameters
def choices(self, cl):
return []
def queryset(self, request, queryset):
origin_GET = request.GET.copy()
fake_GET = QueryDict(mutable=True)
fake_GET.update(self.used_parameters)
request.GET = fake_GET
all_params = {}
for spec in self.get_filters(request, self.used_parameters):
if spec and spec.has_output():
all_params.update(spec.used_parameters)
try:
query_params = [Q((key, value)) for key, value in all_params.items()]
queryset = queryset.filter(reduce(operator.or_, query_params))
except TypeError as e:
pass
# restore
request.GET = origin_GET
return queryset
class OrFilter(OrListFilter):
title = 'Or filter'
parameter_prefix = 'or1'
fields = ("foobar", "foofie")
class FooAdmin(admin.ModelAdmin):
list_filter = (OrFilter, )
app_name/templates/admin/app_name/change_list.html:
{% extends "admin/change_list.html" %}
{% load i18n admin_list %}
{% block filters %}
{% if cl.has_filters %}
<div id="changelist-filter">
<h2>{% trans 'Filter' %}</h2>
{% for spec in cl.filter_specs %}
{% if spec.filter_specs %}
{% admin_list_filter cl spec %}
<ul>
{% for sub_spec in spec.filter_specs %}
<li>{% admin_list_filter cl sub_spec %}</li>
{% endfor %}
</ul>
{% else %}
{% admin_list_filter cl spec %}
{% endif %}
{% endfor %}
</div>
{% endif %}
{% endblock %}
我刚刚发现了一个第三方应用程序,它可能适合您的要求 它有: 原野 或者是添加到每个可用规则中的附加字段 田地 它允许使用OR语句构造查询。你可以用它 创建一个“空”规则,该字段“介于”一组1或多个字段之间 规则 我已经运行了一个测试,添加一个
或字段将起作用。
这是屏幕截图:
是一款Django应用程序,我在搜索了许多类似本文的帖子后,编写了该应用程序来满足这一要求
MultipleEchoiceListFilter扩展了SimpleListFilter,允许您对多个选项进行筛选
用户界面使用可点击链接从“或”查询中“包括”和“排除”选项,而不是勾选/取消勾选复选框。因此,在每次单击之后,您必须等待到服务器的往返,并等待页面刷新。这可能是一个性能/用户体验问题,尤其是对于大量对象
SimpleListFilter保留了“全部”链接和每个选项链接的行为,即,您可以将过滤器重置为所有选项或仅一个选项
当前包含的选项在过滤器中突出显示(在下面的屏幕截图中以蓝色显示)
模板是可重写的,因此可以更改接口以满足需要。就我个人而言,我认为在选项名称和include/exclude链接之间留出一点空间可能有助于区分两者。或者,开关图标可能比单词“include”/“exclude”更直观
您能否向我们提供您的型号和管理员,或一些可能的示例,说明您的确切期望。
all_params = {}
for spec in self.get_filters(request, self.used_parameters):
if spec and spec.has_output():
all_params.update(spec.used_parameters)
try:
query_params = [Q((key, value)) for key, value in all_params.items()]
queryset = queryset.filter(reduce(operator.or_, query_params))
except TypeError as e:
pass