我们正在尝试为 Django API 返回一个标题列表,其中标题可以有几个关键字。

例如,如果我们使用 __icontains 方法搜索“money”和“world”( api.com/?keyworld=money&keyword=world ),这将返回包含money、world 或两者的所有记录。

相关的SQL语句是:

select * from news
    where news_source = 1 or news_source = 2
        and news_title like '%money%' or news_title like '%world%'

我们正在尝试使用此代码来允许用户为 __icontains 以及多个来源拥有多个关键字,因此最终目标是:
api.com/?keyworld=money&keyword=world&source=1&source=2

我们的代码:
def get_queryset(self):
    queryset = News.objects.all()
    title = self.request.query_params.getlist('title')
    source = self.request.query_params.getlist('source')
    if title:
        queryset = queryset.filter(news_title__icontains=title, news_source__in=source)
    return queryset

问题是这仅在使用第二个关键字时返回第二个关键字,而不是在 &keyword= 中键入的内容之前的其他关键字。

最佳答案

您不能使用列表执行 __icontains,但您可以例如设计一个函数,为列表构造这些值的逻辑或。例如:

from django.db.models import Q
from functools import reduce
from operator import or_

def or_fold(list_of_qs):
    if list_of_qs:
        return reduce(or_, list_of_qs)
    else:
        return Q()

def unroll_lists_or(qs, **kwargs):
    return qs.filter([
        or_fold(Q(**{k: vi}) for vi in v)
        for k, v in kwargs.items()
    ])

然后,您可以使用查询集调用 unroll_lists_or,并且每个项目都应该是可迭代的(例如列表)。然后它将执行列表项之间的 or 逻辑,以及不同键之间的 and 逻辑。如果可迭代对象为空,则将其忽略。

所以我们可以把支票写成:
unroll_lists_or(queryset, news_title__icontains=title, news_source=source)

如果 title 包含两个项目(所以 title == [title1, title2] ),并且 source 包含三个项目(所以 source = [source1, source2, source3] ),那么这将导致:
qs.filter(
    Q(news_title__icontains=title1) | Q(news_title__icontains=title2),
    Q(news_source=source1) | Q(news_source=source2) | Q(news_source=source3)
)

但是,您可以将它与 .filter(..) 结合起来进行 __in 检查。例如:
queryset = News.objects.all()
if source:
    queryset = queryset.filter(news_source__in=source)
queryset = unroll_lists_or(queryset, news_title__icontains=title)

关于python - icontains 和 getlist django python,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/51031397/

10-12 21:12