Django从字段开始

时间:2012-08-10 15:28:52

标签: django filter startswith

假设我有一个带有邮政编码字段的地址模型。我可以使用以下行来查找以“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

6 个答案:

答案 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来做到这一点:

  1. django.db.models.sql.Query.query_terms
  2. django.db.models.fields.Field.get_prep_lookup
  3. django.db.models.fields.Field.get_db_prep_lookup
  4. django.db.models.sql.where.WhereNode.make_atom
  5. 刚定义的自定义查找'_ 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