如何在 Django 中使用字段名称、条件和值进行动态过滤

Posted

技术标签:

【中文标题】如何在 Django 中使用字段名称、条件和值进行动态过滤【英文标题】:How to dynamically filter with field name, condition and values in Django 【发布时间】:2020-03-28 21:16:34 【问题描述】:

我有个需求,就是从客户端我会得到一个对象数组,里面有字段名、过滤条件和过滤值这样的。

对象数组示例:

[
 field_name: "book_name", filter_condition: "contains", filter_value: "some book name",
 field_name: "book_category", filter_condition: "not_equal", filter_value: "category value",
 field_name: "book_author", filter_condition: "starts_with", filter_value: "authoer name",
 field_name: "book_price", filter_condition: "equal", filter_value: 100
]

我必须根据上述所有条件进行过滤。考虑我有一个名为 Book 的模型和对象数组中的字段(即 book_name、book_category、book_author、book_price)。对于 filter_condition,我编写了一个比较 filter_condition 并分配 Django 查询集 API 的函数。例如。

def assign_filter_condition(self, request, filter_condition, field_name, filter_value):

   if filter_condition == "contains":
        kwargs = 
                '0__icontains'.format(field_name): filter_value
            
        return kwargs
       ...

这里我不知道如何为 exclude 和 NOT 查询应用条件。

我真的不明白如何为此编写逻辑。谁能帮忙解释一下逻辑。

提前谢谢你。

【问题讨论】:

我不知道您的使用案例或您的工作条件,但您至少应该有理由拒绝使用 Django-filter django-filter.readthedocs.io/en/master/index.html @nigel222 我在问题中提到的上述对象数组示例,就像我将向 DRF 发送请求一样。因为您可以看到过滤条件和字段名称会发生​​变化。所以我不知道如何解决它或在后端编写逻辑。 好的,Django-filter 可能不是适合您使用的工具 (DRF)。希望评论会通知到达这里搜索“动态过滤器”或类似内容的人。 【参考方案1】:

您可以使用 dict 解包来做到这一点 请注意你不需要条件所以是一个字典,数组就足够了 以下代码将引导您前往您想要的地方。

conditions = [
 ("book_name", "contains",  "some book name"),
 ("book_category", "not_equal",  "category value"),
 ("book_author", "starts_with",  "authoer name"),
 ("book_price", "equal",  100)
]
def get_filter(values):
    name,condition,value = values
    key = f"name__condition"
    return key, value

filters = dict(map(get_filter,conditions))
qs = qs.filter(**filters)

【讨论】:

【参考方案2】:

在浏览了几篇 *** 帖子后,我弄清楚了其中的逻辑。所以下面是逻辑。

def get_filter(self, field_name, filter_condition, filter_value):
        # thanks to the below post
        # https://***.com/questions/310732/in-django-how-does-one-filter-a-queryset-with-dynamic-field-lookups
        # the idea to this below logic is very similar to that in the above mentioned post
        if filter_condition.strip() == "contains":
            kwargs = 
                '0__icontains'.format(field_name): filter_value
            
            return Q(**kwargs)

        if filter_condition.strip() == "not_equal":
            kwargs = 
                '0__iexact'.format(field_name): filter_value
            
            return ~Q(**kwargs)

        if filter_condition.strip() == "starts_with":
            kwargs = 
                '0__istartswith'.format(field_name): filter_value
            
            return Q(**kwargs)
        if filter_condition.strip() == "equal":
            kwargs = 
                '0__iexact'.format(field_name): filter_value
            
            return Q(**kwargs)

        if filter_condition.strip() == "not_equal":
            kwargs = 
                '0__iexact'.format(field_name): filter_value
            
            return ~Q(**kwargs)

def get(self, request):
    # getting the array of objects data to filter. The array of objects data 
    # example is in the question
    filter_data = request.query_params.getlist('filterData[]')

    all_books = Books.objects.all()
    # Creating initial Q object
    filter_objects = Q()

    # Looping through the array of objects
    for data in filter_data:
        # The main part. Calling get_filter and passing the filter object data.
        filter_objects &= self.get_filter(
                data["fieldName"], data["filterCondition"],
                data["filterValue"])
    filtered_data = all_books.filter(filter_objects)

希望它可以帮助某人。 :)

【讨论】:

【参考方案3】:

创建一个继承自 DjangoFilterBackends 的类并使用它来代替它

class MyFilterBackend(DjangoFilterBackend):
    def get_filterset_kwargs(self, request, queryset, view):
        data = request.query_params
        data._mutable = True
        for item in data:
            try:
                print(item)
                field = getattr(queryset.model, item)
                field: DeferredAttribute
                data[item] = [x for x in list(field.field.choices) if x[1] == data[item]][0][0]
            except:
                pass
        return 
            'data': request.query_params,
            'queryset': queryset,
            'request': request,
        

【讨论】:

以上是关于如何在 Django 中使用字段名称、条件和值进行动态过滤的主要内容,如果未能解决你的问题,请参考以下文章

Django ORM:未继承子级的字段和值。对象重复。 (使用Django管理界面)

Django 模板 - 按名称访问 M2M 属性和值

如何使用 django rest 框架在模板中获取模型选择字段名称而不是 id

如何使用无效标识符作为 django 表单字段名称

如何根据名称和值检查单选按钮?

如何使用石墨烯-django 对多个字段中的图标使用 OR 条件