2012-09-07 164 views
46

我想我过滤模型从文本 东西的长度的基础上,像Django的过滤器

MyModel.objects.filter(len(text) > 10) 

,其中文本是一个字符或文本字段中为MyModel模型

+0

使用https://docs.djangoproject.com/en/dev/ref/models/database-functions/#django.db.models.functions.Length功能。 – guettli

回答

4

这将是更好的更快,如果你只是增加一列,预先计算(memoizes)的长度文本的 。

例如

class MyModel(models.Model): 
    text = models.TextField() 
    text_len = models.PositiveIntegerField() 

    def save(self, *args, **kwargs): 
     self.text_len = len(self.text) 
     return super(MyModel, self).save(*args, **kwargs) 

MyModel.objects.filter(text_len__gt = 10)  # Here text_len is pre-calculated by us on `save` 
+0

是因为文本字段没有编入索引并且每次计算文本长度查询到达数据库的时间。 lain建议的解决方案也是这样做的,不是吗(尽管这个解决方案对我来说并不合适)。 – ashish

+0

@ashish 1)是的,它是预先计算的。 2)没有拉宁不这样做。 – rantanplan

+0

1)所以如果长度是预先计算,那么为什么我需要另一列2)如果字符发生大于n,lain的解决方案不检查每个表达式? – ashish

27

您可以使用正则表达式过滤器来搜索特定长度的文本:

MyModel.objects.filter(text__regex = r'.{10}.*') 

警告:为MySQL,最大长度为255,否则会抛出异常:

DatabaseError: (1139, "Got error 'invalid repetition count(s)' from regexp") 
+1

作为[文档](https://docs.djangoproject.com/en/dev/ref/models/querysets/#regex)说:'使用原始字符串(例如,r'foo'而不是'foo')来传递在正则表达式语法是建议。“# –

+0

@goliney谢谢你,你是正确的。我编辑了我的答案。 –

+0

我在执行代码 **后发现此异常** OperationalError:(1139,“从regexp得到错误'无效重复计数”)** 这是因为大括号。 – ashish

49

另一种方法是:

MyModel.objects.extra(where=["CHAR_LENGTH(text) > 300"]) 

这可以用在文本lenght过超过255个字符。

+3

如果你有sqlite,它是'LENGTH(..)'。 –

89

Django的> = 1.8可以使用Length function,这是@ Pratyush的CHAR_LENGTH()引擎盖为MySQL下,或LENGTH()其他一些数据库:

from django.db.models.functions import Length 
qs = MyModel.objects.annotate(text_len=Length('text_field_name')).filter(
    text_len__gt=10) 
+1

假设我不想过滤查询集,而是首先返回对象'text_len__gt = 10'('order_by')。任何提示? – vabada

+3

@dabad,你可以像使用任何其他数据库*字段*一样使用'text_len' *注解*,所以它可以在'order_by'或'Sum'或其他任何内容中使用。按照递减的文本长度顺序排序结果并返回长度值:'MyModel.objects.annotate(text_len = Length('text_field_name'))。order_by(' - text_len')。values_list('text_len',flat = True) '。 – hobs

+0

@guettli一个被接受的答案的问题是,原始的海报已经在2015年9月上次被看到,并且您令人钦佩的利他主义是唯一的可能性:-)我必须先编辑这个答案,然后才可以上传。我添加了一个类似的[Django> = 1.9](https://stackoverflow.com/a/45260608/448474),它不需要注释,而是一个全局注册的“Length”变换。 – hynekcer

8

Django的一个不错的解决办法> = 1.9可以通过注册内置函数Length作为转换查找CharField。 (见文档为Length as a transform完全相同的例子)

from django.db.models import CharField 
from django.db.models.functions import Length 

CharField.register_lookup(Length, 'length') 

result = MyModel.objects.filter(text__length__gt=10) 

它可以正确处理所有的后端,通过LENGTH()大多数后端编译和CHAR_LENGTH()为MySQL。然后它自动注册CharField的所有子类,例如为EmailField。 TextField必须单独注册。注册名称“长度”是安全的,因为变换名称永远不会被同名的字段名称或相关字段名称遮蔽或遮蔽。

唯一的缺点可能是可读性难题:“长度”从哪里来? (查找是全球性的,但同样可以幸运地安全地在多个模块反复注册,如果可读性是有用的,而不会在查询运行任何可能的开销。)

其他类似的有价值的解决方案是hobs的上面是短如果注册计数并且不重复使用类似的查询。

+0

@guettli意外的是,在你开始赏金之前,你已经写了一个解决方案,并且一分钟之内?我也是以一种奇怪的顺序来做的:我从Django源中找到了解决方案的详细信息,然后发现它最后都是在关于最终知道解决方案的文档中。 – hynekcer

+0

自从被接受的问题开始以来,我仍然很高兴,但仍然不幸处于顶端,已经过时了。我希望长度函数(> = Django 1.8)的答案越来越多。 AFAIK这件事发生了,但不幸的是,过时的答案仍然处于顶端。 – guettli

-3

我会解决您的应用程序服务器上的问题,而不是税收您的数据库。你可以这样做:

models_less_than_ten = [] 
mymodel = MyModel.objects.all() 
for m in mymodel: 
    if len(m.text) > 10: 
      models_less_than_ten.append(m) 
+0

这对MyModel中的很多行不会很好地扩展。如果你有100,000行,那么对数据库执行strlen并决定不发送一行数据的话,会比向应用程序服务器发送大量数据以过滤掉更少。在db上完成这项工作几乎总是比较好的,如果它太慢或征税,查询可以优化。 – nevelis