Django
Querysets
Suche…
Einführung
Ein Queryset
ist im Queryset
eine Liste von Objekten, die von einem Model
durch Zusammenstellung von Datenbankabfragen abgeleitet werden.
Einfache Abfragen für ein Standalone-Modell
Hier ist ein einfaches Modell, mit dem wir einige Testabfragen ausführen:
class MyModel(models.Model):
name = models.CharField(max_length=10)
model_num = models.IntegerField()
flag = models.NullBooleanField(default=False)
Holen Sie sich ein einzelnes Modellobjekt, bei dem die ID / PK 4 ist:
(Wenn keine Elemente mit der ID 4 vorhanden sind oder mehr als ein Element, wird eine Ausnahme ausgelöst.)
MyModel.objects.get(pk=4)
Alle Modellobjekte:
MyModel.objects.all()
Modellobjekte, deren flag
auf " True
:
MyModel.objects.filter(flag=True)
model_num
mit einer model_num
größer als 25:
MyModel.objects.filter(model_num__gt=25)
Modellieren Sie Objekte mit dem name
"Billigartikel" und dem flag
" False
:
MyModel.objects.filter(name="Cheap Item", flag=False)
Modelle einfache Suche name
für bestimmte Zeichenfolge (Groß- und Kleinschreibung):
MyModel.objects.filter(name__contains="ch")
Modelle einfache Suche name
für bestimmte Zeichenfolge (Groß- und Kleinschreibung):
MyModel.objects.filter(name__icontains="ch")
Erweiterte Abfragen mit Q-Objekten
Angesichts des Modells:
class MyModel(models.Model):
name = models.CharField(max_length=10)
model_num = models.IntegerField()
flag = models.NullBooleanField(default=False)
Wir können Q
Objekte verwenden, um AND
, OR
Bedingungen in Ihrer Suchabfrage zu erstellen. model_num>15
wir beispielsweise an, wir wollen alle Objekte mit flag=True
ODER model_num>15
.
from django.db.models import Q
MyModel.objects.filter(Q(flag=True) | Q(model_num__gt=15))
Das obige übersetzt WHERE flag=True OR model_num > 15
für ein AND, das Sie tun würden.
MyModel.objects.filter(Q(flag=True) & Q(model_num__gt=15))
Q
Objekte erlauben uns auch, mit der Verwendung von ~
keine Abfragen zu machen. Nehmen wir an, wir wollten alle Objekte erhalten, die flag=False
AND model_num!=15
, würden wir tun:
MyModel.objects.filter(Q(flag=True) & ~Q(model_num=15))
Wenn Sie Q-Objekte und "normale" Parameter in filter()
, müssen die Q
Objekte an erster Stelle stehen . Die folgende Abfrage sucht nach Modellen mit ( flag
auf True
oder einer Modellnummer größer als 15
) und einem Namen, der mit "H" beginnt.
from django.db.models import Q
MyModel.objects.filter(Q(flag=True) | Q(model_num__gt=15), name__startswith="H")
Hinweis: Q
Objekte können mit jeder Suchfunktion verwendet werden, die Schlüsselwortargumente wie filter
, exclude
und get
übernimmt. Stellen Sie sicher, dass Sie bei Verwendung von get
nur ein Objekt zurückgeben, da sonst die MultipleObjectsReturned
Ausnahme MultipleObjectsReturned
wird.
Reduzieren Sie die Anzahl der Abfragen bei ManyToManyField (n + 1 Problem).
Problem
# models.py:
class Library(models.Model):
name = models.CharField(max_length=100)
books = models.ManyToManyField(Book)
class Book(models.Model):
title = models.CharField(max_length=100)
# views.py
def myview(request):
# Query the database.
libraries = Library.objects.all()
# Query the database on each iteration (len(author) times)
# if there is 100 librairies, there will have 100 queries plus the initial query
for library in libraries:
books = library.books.all()
books[0].title
# ...
# total : 101 queries
Lösung
Verwenden Sie prefetch_related
für ManyToManyField
wenn Sie wissen, dass Sie später auf ein Feld zugreifen müssen, das ein ManyToManyField
Feld ist.
# views.py
def myview(request):
# Query the database.
libraries = Library.objects.prefetch_related('books').all()
# Does not query the database again, since `books` is pre-populated
for library in libraries:
books = library.books.all()
books[0].title
# ...
# total : 2 queries - 1 for libraries, 1 for books
prefetch_related
kann auch für Nachschlagefelder verwendet werden:
# models.py:
class User(models.Model):
name = models.CharField(max_length=100)
class Library(models.Model):
name = models.CharField(max_length=100)
books = models.ManyToManyField(Book)
class Book(models.Model):
title = models.CharField(max_length=100)
readers = models.ManyToManyField(User)
# views.py
def myview(request):
# Query the database.
libraries = Library.objects.prefetch_related('books', 'books__readers').all()
# Does not query the database again, since `books` and `readers` is pre-populated
for library in libraries:
for book in library.books.all():
for user in book.readers.all():
user.name
# ...
# total : 3 queries - 1 for libraries, 1 for books, 1 for readers
Sobald das Abfrageset ausgeführt wurde, können die abgerufenen Daten jedoch nicht geändert werden, ohne erneut auf die Datenbank zuzugreifen. Das Folgende würde beispielsweise zusätzliche Abfragen ausführen:
# views.py
def myview(request):
# Query the database.
libraries = Library.objects.prefetch_related('books').all()
for library in libraries:
for book in library.books.filter(title__contains="Django"):
print(book.name)
Folgendes kann mit einem Prefetch
Objekt optimiert werden, das in Django 1.7 eingeführt wurde:
from django.db.models import Prefetch
# views.py
def myview(request):
# Query the database.
libraries = Library.objects.prefetch_related(
Prefetch('books', queryset=Book.objects.filter(title__contains="Django")
).all()
for library in libraries:
for book in library.books.all():
print(book.name) # Will print only books containing Django for each library
Anzahl der Abfragen im Feld ForeignKey (n + 1 Problem) reduzieren
Problem
Django-Querysets werden faul bewertet. Zum Beispiel:
# models.py:
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
author = models.ForeignKey(Author, related_name='books')
title = models.CharField(max_length=100)
# views.py
def myview(request):
# Query the database
books = Book.objects.all()
for book in books:
# Query the database on each iteration to get author (len(books) times)
# if there is 100 books, there will have 100 queries plus the initial query
book.author
# ...
# total : 101 queries
Der obige Code bewirkt, dass django die Datenbank nach dem Autor jedes Buches abfragt. Dies ist ineffizient und es ist besser, nur eine einzige Abfrage zu haben.
Lösung
Verwenden Sie select_related
für ForeignKey
wenn Sie wissen, dass Sie später auf ein ForeignKey
Feld zugreifen ForeignKey
.
# views.py
def myview(request):
# Query the database.
books = Books.objects.select_related('author').all()
for book in books:
# Does not query the database again, since `author` is pre-populated
book.author
# ...
# total : 1 query
select_related
kann auch für select_related
verwendet werden:
# models.py:
class AuthorProfile(models.Model):
city = models.CharField(max_length=100)
class Author(models.Model):
name = models.CharField(max_length=100)
profile = models.OneToOneField(AuthorProfile)
class Book(models.Model):
author = models.ForeignKey(Author, related_name='books')
title = models.CharField(max_length=100)
# views.py
def myview(request):
books = Book.objects.select_related('author')\
.select_related('author__profile').all()
for book in books:
# Does not query database
book.author.name
# or
book.author.profile.city
# ...
# total : 1 query
Holen Sie sich SQL für Django Queryset
Das query
in Queryset enthält eine SQL-äquivalente Syntax für Ihre Abfrage.
>>> queryset = MyModel.objects.all()
>>> print(queryset.query)
SELECT "myapp_mymodel"."id", ... FROM "myapp_mymodel"
Warnung:
Diese Ausgabe sollte nur zum Debuggen verwendet werden. Die generierte Abfrage ist nicht backend-spezifisch. Daher werden die Parameter nicht korrekt in Anführungszeichen gesetzt, sodass sie für die SQL-Injection anfällig sind, und die Abfrage kann nicht einmal in Ihrem Datenbank-Backend ausgeführt werden.
Erhalte den ersten und letzten Datensatz von QuerySet
Erstes Objekt erhalten:
MyModel.objects.first()
Letzte Objekte bekommen:
MyModel.objects.last()
Filter First Objekt verwenden:
MyModel.objects.filter(name='simple').first()
Filter Last Objekt verwenden:
MyModel.objects.filter(name='simple').last()
Erweiterte Abfragen mit F-Objekten
Ein F () - Objekt repräsentiert den Wert eines Modellfelds oder einer kommentierten Spalte. Es ermöglicht, auf Modellfeldwerte zu verweisen und Datenbankoperationen mit diesen auszuführen, ohne sie tatsächlich aus der Datenbank in den Python-Speicher ziehen zu müssen. - F () Ausdrücke
Es ist sinnvoll, F()
Objekte zu verwenden, wenn Sie in Ihrer Abfrage auf den Wert eines anderen Feldes verweisen müssen. F()
Objekte bedeuten an sich nichts und können und dürfen nicht außerhalb eines Abfragesets aufgerufen werden. Sie werden verwendet, um auf den Wert eines Felds in demselben Abfragesatz zu verweisen.
Zum Beispiel ein Modell gegeben ...
SomeModel(models.Model):
...
some_field = models.IntegerField()
Ein Benutzer kann Objekte abfragen, bei denen der Wert von some_field
doppelt so some_field
ist wie seine id
indem er auf den Wert des id
Felds verweist und dabei mit F()
some_field
:
SomeModel.objects.filter(some_field=F('id') * 2)
F('id')
referenziert einfach den id
Wert für dieselbe Instanz. Django verwendet es, um eine entsprechende SQL-Anweisung zu erstellen. In diesem Fall ähnelt dies etwas:
SELECT * FROM some_app_some_model
WHERE some_field = ((id * 2))
Ohne F()
Ausdrücke würde dies entweder mit Raw-SQL oder durch Filtern in Python erreicht werden (was die Leistung verringert, insbesondere wenn viele Objekte vorhanden sind).
Verweise:
Aus der Definition der Klasse F()
:
Ein Objekt, das Verweise auf vorhandene Abfrageobjekte auflösen kann. - F-Quelle
Hinweis: Dieses Beispiel stammt aus der Antwort, die mit Zustimmung von TinyInstance oben aufgeführt wurde.