Ich möchte mein Modell anhand der Länge des Textes filtern
MyModel.objects.filter(len(text) > 10)
Dabei ist Text ein Zeichen- oder Textfeld im MyModel-Modell
Antworten:
Es wäre viel besser und schneller, wenn Sie nur eine Spalte hinzufügen, die die Länge des Textes vorberechnet (auswendig lernt).
z.B
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`
text. Dies wird jedes Mal aktualisiert, wenn der Text geändert wird. Wenn wir also das Modell abfragen, können wir nach der Textlänge filtern, die WIR für unsere saveMethode vorberechnet haben .
Für Django> = 1.8 können Sie die Length-Funktion verwenden , die @ Pratyush CHAR_LENGTH()unter der Haube für MySQL oder LENGTH()für einige andere Datenbanken ist:
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=10an erster Stelle zurück ( order_by). Irgendein Hinweis?
text_len Annotation in der gleichen Art und Weise Sie eine andere Datenbank verwenden würden Feld , so dass es in funktioniert order_byoder Sumoder was auch immer. So sortieren Sie die Ergebnisse in abnehmender Reihenfolge der Textlänge und geben die Längenwerte zurück : MyModel.objects.annotate(text_len=Length('text_field_name')).order_by('-text_len').values_list('text_len', flat=True).
LengthTransform.
Sum. Es ist auch in vielen Fällen äußerst wichtig. Ich hatte einen Fall, in dem ich die maximale Datengröße überprüfen musste, die eine Abfrage möglicherweise zurückgibt. Der Server verfügt nicht über genügend Arbeitsspeicher, und eine Variante davon funktionierte einwandfrei.
Ein anderer Weg ist:
MyModel.objects.extra(where=["CHAR_LENGTH(text) > 300"])
Dies kann verwendet werden, wenn die Textlänge ebenfalls mehr als 255 Zeichen beträgt.
LENGTH(..).
Eine gute Lösung für Django> = 1.9 ist möglich, indem die eingebaute Funktion Lengthals Transformation für die CharFieldSuche registriert wird .
Registrieren Sie die Transformation einmal im Projekt. (Der beste Ort ist wahrscheinlich models.py.)
from django.db.models import CharField
from django.db.models.functions import Length
CharField.register_lookup(Length, 'length')
Verwendung :
result = MyModel.objects.filter(text__length__gt=10)
Siehe genau das gleiche Beispiel in den Dokumenten für Länge als Transformation .
Es funktioniert korrekt für alle Backends, kompiliert von LENGTH()für die meisten Backends und von CHAR_LENGTH()für MySQL. Es wird dann automatisch für alle Unterklassen von CharField registriert, z. B. für EmailField. Die TextFieldmüssen einzeln registriert werden. Es ist sicher, den Namen "Länge" zu registrieren, da ein Transformationsname niemals durch einen gleichnamigen Feldnamen oder einen verwandten Feldnamen schattiert oder schattiert werden kann.
Der einzige Nachteil könnte das Lesbarkeitsrätsel sein: Woher kommt die "Länge"? (Die Suche ist global, kann jedoch glücklicherweise wiederholt in mehreren Modulen sicher registriert werden, wenn dies für die Lesbarkeit nützlich ist, ohne dass ein möglicher Overhead zur Laufzeit der Abfrage möglich ist.)
Eine andere ähnlich wertvolle Lösung sind die oben genannten Kochfelder , die kürzer sind, wenn eine Registrierung zählt und wenn eine ähnliche Abfrage nicht wiederholt verwendet wird.
Mit dem Regex-Filter können Sie nach Text einer bestimmten Länge suchen:
MyModel.objects.filter(text__regex = r'.{10}.*')
Vorsichtsmaßnahme: Für MySQL beträgt der maximale Längenwert 255. Andernfalls wird eine Ausnahme ausgelöst:
DatabaseError: (1139, "Got error 'invalid repetition count(s)' from regexp")
Using raw strings (e.g., r'foo' instead of 'foo') for passing in the regular expression syntax is recommended.
Ich würde das Problem auf Ihrem App-Server lösen und Ihre Datenbank nicht besteuern. Sie können dies tun durch:
models_less_than_ten = []
mymodel = MyModel.objects.all()
for m in mymodel:
if len(m.text) > 10:
models_less_than_ten.append(m)