如何在 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管理界面)