假设我有一个带有邮政编码字段的地址模型。我可以使用以下行来查找以“123”开头的邮政编码地址:
Address.objects.filter(postcode__startswith="123")
现在,我需要搜索“其他方式”。我有一个带有postcode_prefix字段的地址模型,我需要检索postcode_prefix是给定代码的前缀的所有地址,如“12345”。因此,如果在我的数据库中我有2个地址使用postcode_prefix =“123”和“234”,则只会返回第一个地址。
类似的东西:
Address.objects.filter("12345".startswith(postcode_prefix))
问题是这不起作用。 我能想出的唯一解决方案是对第一个char执行过滤,例如:
Address.objects.filter(postcode_prefix__startswith="12345"[0])
然后,当我得到结果时,制作一个列表理解,正确过滤它们,如下所示:
results = [r for r in results if "12345".startswith(r.postcode_prefix)]
在django中有更好的方法吗? 谢谢, 的Fabrizio
答案 0 :(得分:20)
我认为你试图用你的“类似”行写的东西写得恰到好处:
Address.objects.filter(postcode__startswith=postcode_prefix)
答案 1 :(得分:9)
在SQL术语中,您希望实现的内容如下所示('12345'是您要搜索的邮政编码):
SELECT *
FROM address
WHERE '12345' LIKE postcode_prefix||'%'
这不是一个真正的标准查询,我认为没有任何可能只使用get()/ filter()在Django中实现这一点。
但是,Django提供了一种使用extra()
提供其他SQL子句的方法:
postcode = '12345'
Address.objects.extra(where=["%s LIKE postcode_prefix||'%%'"], params=[postcode])
请参阅Django documentation on extra()以获取进一步的参考。另请注意,extra包含纯SQL,因此您需要确保该子句对您的数据库有效。
希望这适合你。
答案 2 :(得分:2)
有点满口,但你可以通过注释你的搜索值然后过滤它来做到这一点。所有这些都在数据库中很快发生。
from django.db.models import Value as V, F, CharField
MyModel.objects.annotate(
search_str=Value('...', output_field=CharField())
).filter(
search_str__istartswith=F('my_previx_field')
)
我确定你现在也可以通过一个漂亮的模板函数来做到这一点......但这对我来说足够干净了。
答案 3 :(得分:0)
一个。如果不是问题https://code.djangoproject.com/ticket/13363, 你可以这样做:
queryset.extra(select={'myconst': "'this superstring is myconst value'"}).filter(myconst__contains=F('myfield'))
也许,他们会修复一个问题并且可以解决问题。
B中。如果不是问题16731(抱歉没有提供完整的URL,没有足够的代表,请参阅上面的另一张票),您可以使用添加了“.annotate”的字段进行过滤,并创建自定义聚合函数,如下所示: http://coder.cl/2011/09/custom-aggregates-on-django/
℃。最后成功。我设法使用以下的monkeypatching来做到这一点:
刚定义的自定义查找'_ starts',其反向逻辑为' _startswith'
答案 4 :(得分:0)
可能的替代方案。 (不知道它与已接受的解决方案相比如何,在执行时间内将列作为第二个参数)
q=reduce(lambda a,b:a|b, [Q(postcode__startswith=postcode[:i+1]) for i in range(len(postcode))])
因此,您生成所有前缀,或者它们一起生成......
答案 5 :(得分:0)
执行您需要的原始 SQL 查询如下所示:
select * from postal_code_table where '1234567' like postal_code||'%%'
此查询将从您的表中选择任何 postal_code,它是“1234567”的子字符串,并且必须从头开始,即:“123”、“1234”等。
现在要在 Django 中实现这一点,首选方法是使用自定义查找:
from django.db.models.fields import Field
from django.db.models import Lookup
@Field.register_lookup
class LowerStartswithContainedBy(Lookup):
'''Postgres LIKE query statement'''
lookup_name = 'istartswithcontainedby'
def as_sql(self, compiler, connection):
lhs, lhs_params = self.process_lhs(compiler, connection)
rhs, rhs_params = self.process_rhs(compiler, connection)
params = lhs_params + rhs_params
return f"LOWER({rhs}) LIKE LOWER({lhs}) || '%%'", params
现在您可以编写如下的 Django 查询:
PostCode.objects.filter(code__istartswithcontainedby='1234567')
同理,如果你只是找子串,不需要startswith条件,只需将as_sql方法的返回行修改如下:
return f"LOWER({rhs}) LIKE '%%' || LOWER({lhs}) || '%%'", params