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

How to dynamically filter with field name, condition and values in Django

我有一个要求,就是从客户端获取一个对象数组,其中包含字段名称、过滤条件和过滤值,如下所示。

示例对象数组:

[
 {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 query的条件怎么申请

我真的不明白这个逻辑怎么写。任何人都可以帮助逻辑。

提前致谢。

你可以使用 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)

在浏览了几篇 Whosebug 帖子后,我明白了其中的逻辑。所以下面是逻辑。

def get_filter(self, field_name, filter_condition, filter_value):
        # thanks to the below post
        # 
        # 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)

希望对大家有所帮助。 :)

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

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