django在lookup中包含__

fivyi3re  于 2023-06-25  发布在  Go
关注(0)|答案(6)|浏览(138)

所以我想找到任何类型的匹配给定的一些字段,所以例如,这是我想做的:

possible_merchants = ["amazon", "web", "services"]
# Possible name --> "Amazon Service"
Companies.objects.filter(name__icontains__in=possible_merchants)

遗憾的是,在查找中不可能混合使用icontains和__。
这似乎是一个相当复杂的查询,所以如果我至少可以忽略名称的大小写就足够了,例如:

Companies.objects.filter(name__ignorecase__in=possible_merchants)

有什么想法吗
P.D.:我发布的查询不起作用,这只是表达我需要的一种方式(以防万一)

lymgl2op

lymgl2op1#

你可以用Q构造函数创建查询集,然后用|运算符合并它们以得到它们的并集:

from django.db.models import Q

def companies_matching(merchants):
    """
    Return a queryset for companies whose names contain case-insensitive
    matches for any of the `merchants`.
    """
    q = Q()
    for merchant in merchants:
        q |= Q(name__icontains = merchant)
    return Companies.objects.filter(q)

(And类似地,使用iexact代替icontains。)

b1payxdu

b1payxdu2#

我发现使用reduceor_操作符是一种更清晰的方法:

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

def get_companies_from_merchants(merchant_list):
    q_object = reduce(or_, (Q(name__icontains=merchant) for merchant in merchant_list))
    return Companies.objects.filter(q_object)

这将创建一个Q对象列表,查询name以在商家列表中包含单个元素。这将发生在merchant_list中的所有元素,并且所有这些Q对象将被简化为具有多个OR的单个Q对象,可以直接应用于过滤器查询。

rjzwgtxy

rjzwgtxy3#

这是我采用的方法:

class MyManager(models.Manager):
    def exclusive_in(self, lookup, value_list):
        return self.filter(reduce(or_, (Q(**{lookup:_}) for _ in value_list)))

下面是现在使用它:

Companies.objects.exclusive_in('name__icontains', possible_merchants])

它的灵感来自于这个线程中的其他答案,以及Django filter queryset __in for every item in list。

h5qlskok

h5qlskok4#

另一种方法是模拟Django通常对iexact查询所做的操作(它通过SQL Upper函数将比较语句的两个部分都转换为大写。
这样,查询将如下所示:

Companies.objects.annotate(
    upper_name=models.Upper("name")
).filter(
    upper_name__in=[rchant.upper() for merchant in possible_merchants]
)
5t7ly7z5

5t7ly7z55#

Gareth Rees的回答在使用django-filter包时对我的类似问题有很大帮助。
要在django_filters中以通用的方式使用它,可以像这样创建一个MultipleInputs过滤器:

class MultipleInputs(filters.BaseInFilter, filters.CharFilter):
        pass

然后在filterset中使用它,并使用自定义过滤方法:

from django.db.models import Q
    
    class MyFilter(FilterSet):
        search = MultipleInputs(field_name='longname', label='Text search',
            method='multiplesearch', 
            help_text="Free text search. May be a comma separated list of strings.")
 
        def multiplesearch(self, queryset, field_name, value):
            q = Q()
            for searchstring in value:
                arguments = {field_name+'__icontains' : searchstring}

                q |= Q(**arguments)
            return queryset.filter(q)

任何MultiInputs字段都不能有逗号分隔的输入。如果您继承了MyFilter,但使用具有不同field_name的Filter覆盖搜索,也可以使用。

vohkndzv

vohkndzv6#

使用iregex将是:

regex = '|'.join(['match 1', 'match 2', 'match 3'])
Companies.objects.filter(name__iregex=f'({regex})')

你甚至可以添加lookaround,这样就可以保证匹配的是单个单词,而不是其他单词的不同含义的部分,即:

options = ['match 1', 'match 2', 'match 3']
regex_options = [f'(?<!\w){option}(?!\w)' for option in options]  # Adds lookaround
regex = '|'.join(regex_options)
Companies.objects.filter(name__iregex=f'({regex})')

lookaround(即lookbehind + lookahead)不会引起字符消耗,因此它将与子字符串开始或结束字符串匹配,这在[^\w]中是不可能的。

相关问题