【问题标题】:django filter on the basis of text length基于文本长度的django过滤器
【发布时间】:2012-09-01 02:31:05
【问题描述】:

我想根据文本的长度过滤我的模型 像

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

其中 text 是 MyModel 模型中的 Char 或 Text 字段

【问题讨论】:

标签: django


【解决方案1】:

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

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

警告:对于 MySQL,最大长度值为 255。否则抛出异常:

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

【讨论】:

  • 正如documentation 所说:Using raw strings (e.g., r'foo' instead of 'foo') for passing in the regular expression syntax is recommended.
  • 执行代码后出现此异常OperationalError: (1139, "Got error 'invalid repeat count(s)' from regexp") 这是因为 curly大括号。
  • 其实上面说的异常基本都是mysql异常。
  • 这适用于任何低于 256 的数字。MySQL 的最大重复计数为 256。
  • @emil-stenstrom 实际上是 255
【解决方案2】:

如果你只添加一个预先计算(记忆)长度的列会更好并且更快 文本。

例如

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`

【讨论】:

  • 是不是因为文本字段没有被索引,并且每次查询访问数据库时都会计算文本长度。 lain 建议的解决方案也是如此,不是吗(尽管该解决方案对我不起作用)。
  • @ashish 1) 是的,它是预先计算好的。 2) 没有 lain 不会做同样的事情。
  • 1) 所以如果长度是预先计算好的,那么为什么我需要另一列 2) 如果字符出现大于 n,lain 的解决方案不会检查每个表达式??跨度>
  • @ashish 我在上面代码的最后一行添加了注释。我们在模型中添加一列来存储text 的长度。每次修改文本时都会更新。因此,当我们查询模型时,我们可以根据我们在 save 方法中预先计算的文本长度进行过滤。
【解决方案3】:

另一种方式是:

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

这也可以用于文本长度超过 255 个字符的情况。

【讨论】:

  • 如果你有 sqlite,它是LENGTH(..)
【解决方案4】:

对于 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)

【讨论】:

  • 假设我不想过滤查询集,而是首先返回对象text_len__gt=10 (order_by)。有什么提示吗?
  • @dabad,您可以像使用任何其他数据库字段一样使用text_len annotation,因此它可以在@ 987654330@ 或 Sum 或其他。以文本长度递减顺序对结果进行排序并返回长度值:MyModel.objects.annotate(text_len=Length('text_field_name')).order_by('-text_len').values_list('text_len', flat=True)
  • @guettli 接受的答案的一个问题是,原始海报已于 2015 年 9 月在 SO 上一次看到,而您令人钦佩的利他主义是唯一的可能性 :-) 我之前不得不编辑这个答案我可以投票。我添加了一个类似的answer for Django >= 1.9,不需要注解,而是全局注册Length Transform。
  • 这在文档中很难找到,因为它没有与 Sum 等其他聚合组合在一起。对于许多情况,它也非常重要。我有一个案例,我需要预先检查查询可能返回的最大数据大小不会使服务器内存不足,而这种情况的一个变体完美地工作。
  • @AlanSE 他们没有用 Sum 和其他聚合记录它的原因是因为它不是 聚合。它适用于单个记录(行)。结果查询集的行数与运行 Length 运算符之前的行数相同。所以它被称为transformationmapping。聚合减少记录数。 映射没有。
【解决方案5】:

通过将内置函数 Length 注册为 Transform 用于 CharField 查找,可以为 Django >= 1.9 提供一个不错的解决方案。

注册项目中的转换一次。 (最好的地方可能是models.py。)

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 as a transform 的文档中查看完全相同的示例。


它适用于所有后端,LENGTH() 编译大多数后端,CHAR_LENGTH() 编译 MySQL。然后自动为 CharField 的所有子类注册它,例如对于电子邮件字段。 TextField 必须单独注册。注册名称“length”是安全的,因为转换名称永远不会被同名的字段名称或相关字段名称遮蔽或遮蔽。

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

其他类似有价值的解决方案是上面的hobs,如果注册很重要并且类似的查询没有被重复使用,那么它会更短。

【讨论】:

  • @guettli 没想到你在开始赏金前一分钟就写了一个解决方案?我也以奇怪的顺序完成了它:我从 Django 源代码中找到了解决方案的详细信息,然后发现这一切都在关于最终您首先知道解决方案的文档中。
  • 自从接受的问题(不幸的是仍然是最重要的问题)已经过时以来,我开始了一项赏金活动。我希望 Length 函数 (>= Django 1.8) 的答案能得到更多的支持。 AFAIK 这发生了,但不幸的是,过时的答案仍然是最重要的。
【解决方案6】:

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

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

【讨论】:

  • 这不会很好地扩展到 MyModel 中的许多行。如果您有 100,000 行,则执行 strlen 并决定不发送行比将大量数据发送到应用程序服务器以过滤掉对数据库的负担要小。在数据库上完成工作几乎总是更好,如果它太慢或太费力,可以优化查询。
猜你喜欢
  • 1970-01-01
  • 2015-04-08
  • 1970-01-01
  • 1970-01-01
  • 2020-10-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-25
相关资源
最近更新 更多