Django管理列表“或”条件

2024-10-01 07:47:46 发布

您现在位置:Python中文网/ 问答频道 /正文

很抱歉,如果这个问题以前有人回答,但我做了很多谷歌搜索没有成功。在

我知道如何在管理视图中创建自定义的list_filter(例如,子类化SimpleFilter)。在

我真正想要的,是一种方法(在管理列表视图上)“检查”不同的过滤器,将它们组合在一个或公式中。在

例如,假设您有:

# models.py
class Foo(models.Model):
    foobar = ...
    foofie = ...
...

# admin.py
class FooAdmin(admin.ModelAdmin):
    list_filter = ( "foobar", "foofie" )
...

FooAdmin生成的管理列表视图中,我可以选择按foobar或{}过滤记录。有没有一种方法可以通过公式来过滤它们:foobar = X OR foofie = Y,其中X和{}是{}和{}可以假定的两个值?在

有可能吗?在

我知道在django管理视图中并不是所有事情都是可能的,但这似乎是一个非常常见的请求,我想知道我是否错过了理解或阅读的内容。在

另外,第三方应用程序也可以使用。谢谢:)


Tags: 方法py视图过滤器列表adminmodelsfilter
3条回答

找到了一个解决方案:

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, )

应用程序名称/模板/管理/应用程序名称/更改_列表.html公司名称:

^{pr2}$

从@dima kudosh借了一些代码。在

解释

^{}ModelAdmin.list_filter创建{}s(筛选器规范),然后使用^{}到{a3}。在

^{}使用used_parameters过滤查询集:queryset.filter(**self.used_parameters)。在

因此,我们可以从OrListFilter.fields创建FieldListFilter,并使用它们的used_parameters构造查询:

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管理过滤器的工作原理。当您想在管理页面中过滤查询集时,django会查找所有注册的过滤器。如果使用此值为filter django filter queryset设置值。如果设置多个过滤器django将queryset过滤两次,则等于queryset=查询集过滤器(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')

并在管理员中注册:

^{pr2}$

它不能与标准的django过滤器一起工作,list_filter中的所有值都必须从ORListFilter类继承。它也不能与日期时间过滤器一起工作,但是你可以添加这个功能。在

我刚刚发现了一个第三方应用程序,它是django-advanced-filters,可能符合你的要求。在

它有:

The OR field

OR is an additional field that is added to every rule's available fields.

It allows constructing queries with OR statements. You can use it by creating an "empty" rule with this field "between" a set of 1 or more rules.

我已经运行了一个测试,添加一个OR field就可以了。 这是截图: enter image description here

相关问题 更多 >