[英]django admin list_filter “or” condition
對不起,如果之前已經回答過這個問題,但我做了很多谷歌搜索沒有成功。
我知道如何在管理視圖中創建自定義list_filter
(例如, SimpleFilter
)。
我真正想要的是一種方法(在管理列表視圖中)“檢查”在OR公式中組合它們的不同過濾器。
舉個例子,假設你有:
# models.py
class Foo(models.Model):
foobar = ...
foofie = ...
...
# admin.py
class FooAdmin(admin.ModelAdmin):
list_filter = ( "foobar", "foofie" )
...
在FooAdmin
生成的管理列表視圖中,我可以選擇通過foobar
或foofie
過濾記錄。 有沒有辦法用公式過濾它們: foobar = X OR foofie = Y
,其中X
和Y
是foobar
和foofie
可以假設的兩個值?
它甚至可能嗎?
我知道在django管理員視圖中並非一切皆有可能,但這似乎是一個非常常見的請求,我想知道我是否錯過理解或閱讀的東西。
此外,歡迎使用第三方應用程序。 謝謝 :)
我剛剛發現了第三方應用程序,它是django-advanced-filters ,可能符合您的要求。
它有:
OR字段
OR是添加到每個規則的可用字段的附加字段。
它允許使用OR語句構造查詢。 您可以通過創建一個“空”規則來使用它,該字段“介於”一組1個或多個規則之間。
首先,我嘗試解釋django管理過濾器的工作原理。 當您想要在管理頁面中過濾您的查詢集時,django會查找所有已注冊的過濾器。 如果使用此值為filter django filter queryset設置值。 如果你設置多個過濾器django過濾你的查詢集兩次,這等於queryset = queryset.filter(param1 = 1).filter(param2 = 2)或在SQL中:SELECT ... WHERE param1 = 1 AND 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過濾器,list_filter中的所有值都必須從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 /模板/管理/ 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 %}
借用@ dima-kudosh的一些代碼。
ChangeList.get_filters()
從ModelAdmin.list_filter
創建ListFilter
( ModelAdmin.list_filter
,然后使用ListFilter.queryset()
來get_queryset()
。
FieldListFilter.queryset()
使用used_parameters
來過濾queryset: queryset.filter(**self.used_parameters)
。
所以我們可以從OrListFilter.fields
創建FieldListFilter
並使用他們的used_parameters
來構造OR查詢:
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
Django Admin多選列表過濾器是一個Django應用程序,我寫了滿足這個要求,搜索了很多像這樣的帖子。
MultipleChoiceListFilter擴展了SimpleListFilter,允許您過濾多個選項。
用戶界面使用可點擊鏈接從'或'查詢中選擇'包含'和'排除',而不是勾選/取消勾選復選框。 因此,每次單擊后,您必須等待往返服務器以及刷新頁面。 這可能是性能/ UX問題,尤其是對於大量對象。
“全部”鏈接以及每個選擇鏈接的行為都是從SimpleListFilter中保留的 - 即您可以將過濾器重置為所有選項或僅選擇其中一個。
當前包含的選項在過濾器中突出顯示(在下面的屏幕截圖中為藍色)。
模板是可覆蓋的,因此您可以更改界面以滿足您的需要。 我個人認為選擇名稱和包含/排除鏈接之間的空格可能有助於區分這兩者。 或者,開關圖標可能比單詞“include”/“exclude”更直觀。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.