2012-09-07 162 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