如何过滤Django';s CommaseparatedTegerfield

如何过滤Django';s CommaseparatedTegerfield,django,django-models,filter,field,Django,Django Models,Filter,Field,假设一个模型 class Foo(models.Model): bar = models.CommaSeparatedIntegerField('Filter Me!') 条的内容可能看起来像,例如12,35,67142 我想查询所有Foos,它们在bar中有42: all_42_foos = Foo.objects.filter(bar__contains="42") 这不会给出正确的结果,因为CommaSeparatedIntegerField继承自CharField,并且过滤器

假设一个模型

class Foo(models.Model):
    bar = models.CommaSeparatedIntegerField('Filter Me!')
的内容可能看起来像,例如
12,35,67142

我想查询所有
Foo
s,它们在
bar
中有
42

all_42_foos = Foo.objects.filter(bar__contains="42")
这不会给出正确的结果,因为
CommaSeparatedIntegerField
继承自
CharField
,并且过滤器计算使用字段的字符串内容(也将上述示例与
142
匹配)


在检查
42
之前,如何使用在
栏上执行
.split(“,”
的过滤器?我真的不想把
bar
变成一个
manytomy
,因为这将是一个可怕的开销。

像这样的东西怎么样:

from django.db.models import Q

all_42_foos = Foo.objects.filter( Q(bar__startswith='42,') | Q(bar__endswith=',42') | Q(bar__contains=',42,') | Q(bar__exact='42') )
虽然这是一个有点冗长的查询,但我认为沿着这些思路的东西将是获得所需内容的唯一途径。也许值得将其转化为一个单独的函数

def all_x_foos(x):
    return Foo.objects.filter( Q(bar__startswith=x+',') | Q(bar__endswith=','+x) | Q(bar__contains=',{0},'.format(x)) | Q(bar__exact=x) )

出于好奇,您是否检查了应用程序的实际性能,包括多对多方式,您正在描述的伪多对多方法?

此答案是@desfido答案的扩展。基本上,将对象类型与过滤CommaseOperatedInteger字段的函数解耦。结果表明,此解决方案比使用正则表达式查找正确值更快。我使用了此正则表达式

_regex = r"(^|(\d*,)+)(%s)((,\d*)+|$)" %('|'.join(_ids))
如果要在逗号分隔的值中搜索多个值,可以在循环中调用下面给出的函数

此函数的关键字参数为:-

cs_field_name将是逗号分隔的整数字段的名称

x将是要搜索的整数

它返回一个Q对象,然后可以将其组合并用于过滤

def search_comma_seperated_field(cs_field_name,x):
    startswith_key = cs_field_name + "__startswith"
    endswith_key = cs_field_name + "__endswith"
    contains_key = cs_field_name + "__contains"
    exact_key = cs_field_name + "__exact"
    return Q(**{startswith_key : x+','}) | \
                                     Q(**{endswith_key:','+x}) |\
                                     Q(**{contains_key : ',{0},'.format(x)}) |\
                                     Q(**{exact_key:x})

也可以将其写入一个正则表达式:

all_42_foos = Foo.objects.filter(bar__regex=u'^42,|,42,|,42$|^42$')


谢谢是的,它是冗长的,但它确实起作用。一句话:我们必须测试一个准确的
42,
,因为这对这个字段也是有效的。
bar_number = 42
bar_regex = r'^{0},|,{0},|,{0}$|^{0}$'.format(bar_number)
all_42_foos = Foo.objects.filter(bar__regex=bar_regex)