简体   繁体   English

icontains和getlist django python

[英]icontains and getlist django python

We are trying to return a list of titles for the Django API, in which the title can have a few keywords. 我们正在尝试返回Django API的标题列表,其中标题可以包含几个关键字。

So for instance, if we use the __icontains method to search for "money" and "world" ( api.com/?keyworld=money&keyword=world ) this will return all records that contain money, world or both. 因此,例如,如果我们使用__icontains方法搜索“money”和“world”( api.com/?keyworld=money&keyword=world ),这将返回包含money,world或两者的所有记录。

The related SQL statement is: 相关的SQL语句是:

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

We are trying to use this code to allow the user to have multiple keywords for the __icontains as well as multiple sources, so the end goal is: 我们正在尝试使用此代码来允许用户为__icontains以及多个源提供多个关键字,因此最终目标是:

api.com/?keyworld=money&keyword=world&source=1&source=2

Our code: 我们的代码:

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

The issue is that this is only returning the second keyword if a second keyword is used, and not other keywords prior to what is typed in &keyword= . 问题是,如果使用第二个关键字,则仅返回第二个关键字,而不是在&keyword=键入的关键字之前返回其他关键字。

You can not perform an __icontains with a list, but you can for example design a function that, for a list constructs the logical or of these values. 您不能使用列表执行__icontains ,但是您可以设计一个函数,对于列表构造逻辑或这些值。 For example: 例如:

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()
    ])

You can then call the unroll_lists_or with a queryset, and each item should be an iterable (for example a list). 然后,您可以使用查询集调用unroll_lists_or ,并且每个项应该是可迭代的(例如列表)。 It will then perform or -logic between the items of the list, and and -logic between different keys. 然后它将执行列表的项目之间的编辑逻辑,并不同的键之间编辑逻辑。 In case an iterable is empty, it is ignored. 如果iterable为空,则忽略它。

So we can then write the check as: 所以我们可以把支票写成:

unroll_lists_or(queryset, news_title__icontains=title, news_source=source)

In case title contains two items (so title == [title1, title2] ), and source contains three items (so source = [source1, source2, source3] ), then this will result in: 如果title包含两个项目(所以title == [title1, title2] ),并且source包含三个项目(so 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)
)

You can however combine it with an .filter(..) for the __in check. 但是,您可以将它与.filter(..)用于__in检查。 For example: 例如:

queryset = News.objects.all()
if source:
    queryset = queryset.filter(news_source__in=source)
queryset = unroll_lists_or(queryset, news_title__icontains=title)

I was able to solve this by creating 2 separate functions within the get_querset() function, which is called when a GET request is made. 我能够通过在get_querset()函数中创建2个单独的函数来解决这个问题,该函数在发出GET请求时被调用。

 def get_queryset(self):
        queryset = News.objects.all()
        source_list = self.request.query_params.getlist('source')
        keyword_list = self.request.query_params.getlist('title')
        if source_list or keyword_list:
            def create_q_source(*args):
                list = [*args]
                source = Q()
                for value in list:
                    source.add(Q(news_source=value), Q.OR)
                return source
            def create_q_keyword(*args):
                list = [*args]
                keyword = Q()
                for value in list:
                    keyword.add(Q(news_title__icontains=value), Q.OR)
                return keyword
            queryset = queryset.filter(create_q_source(*source_list),create_q_keyword(*keyword_list))
        return queryset

Edit: When you go to the api link and pass in the parameters, filtering will occur based on what is passed in: 编辑:当您转到api链接并传入参数时,将根据传入的内容进行过滤:

http://127.0.0.1:8000/api/notes/?keyword=trump&keyword=beyond&keyword=money&source=1

SQL Equivalent: SQL等价物:

select * from news where news_source = 1 AND news_title like '%beyond%' OR news_title like '%money%'

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM