Django
Zestawy zapytań
Szukaj…
Wprowadzenie
Zestaw Queryset
jest zasadniczo listą obiektów uzyskanych z Model
poprzez kompilację zapytań do bazy danych.
Proste zapytania w autonomicznym modelu
Oto prosty model, którego użyjemy do uruchomienia kilku zapytań testowych:
class MyModel(models.Model):
name = models.CharField(max_length=10)
model_num = models.IntegerField()
flag = models.NullBooleanField(default=False)
Uzyskaj pojedynczy obiekt modelu, w którym id / pk wynosi 4:
(Jeśli nie ma elementów o identyfikatorze 4 lub jest ich więcej niż jeden, spowoduje to wyjątek).
MyModel.objects.get(pk=4)
Wszystkie obiekty modelu:
MyModel.objects.all()
Modeluj obiekty z flag
ustawioną na True
:
MyModel.objects.filter(flag=True)
Modeluj obiekty o model_num
większej niż 25:
MyModel.objects.filter(model_num__gt=25)
Modeluj obiekty o name
„Tani przedmiot” i ustawiono flag
False
:
MyModel.objects.filter(name="Cheap Item", flag=False)
Modeluje prostą name
wyszukiwania dla określonego ciągu (rozróżniana jest wielkość liter):
MyModel.objects.filter(name__contains="ch")
Modeluje prostą name
wyszukiwania dla określonego ciągu (bez rozróżniania wielkości liter):
MyModel.objects.filter(name__icontains="ch")
Zaawansowane zapytania z obiektami Q.
Biorąc pod uwagę model:
class MyModel(models.Model):
name = models.CharField(max_length=10)
model_num = models.IntegerField()
flag = models.NullBooleanField(default=False)
Możemy użyć obiektów Q
do utworzenia warunków AND
, OR
w zapytaniu wyszukiwania. Na przykład powiedzmy, że chcemy, aby wszystkie obiekty, które mają flag=True
LUB model_num>15
.
from django.db.models import Q
MyModel.objects.filter(Q(flag=True) | Q(model_num__gt=15))
Powyższe przekłada się na WHERE flag=True OR model_num > 15
podobnie dla ORAZ zrobiłbyś.
MyModel.objects.filter(Q(flag=True) & Q(model_num__gt=15))
Obiekty Q
pozwalają nam również wykonywać zapytania NIE przy użyciu ~
. Powiedzmy, że chcieliśmy uzyskać wszystkie obiekty, które mają flag=False
ORAZ model_num!=15
, zrobilibyśmy:
MyModel.objects.filter(Q(flag=True) & ~Q(model_num=15))
Jeśli używasz Q obiektów i „normalnych” parametrów w filter()
, wtedy Q
obiektów musi być na pierwszym miejscu . Poniższe zapytanie wyszukuje modele z ( flag
ustawiona na True
lub numer modelu większy niż 15
) i nazwą zaczynającą się na „H”.
from django.db.models import Q
MyModel.objects.filter(Q(flag=True) | Q(model_num__gt=15), name__startswith="H")
Uwaga: Obiekty Q
mogą być używane z dowolną funkcją wyszukiwania, która pobiera argumenty słów kluczowych, takie jak filter
, exclude
, get
. Upewnij się, że kiedy użyjesz z get
, zwrócisz tylko jeden obiekt, w przeciwnym razie zostanie zgłoszony wyjątek MultipleObjectsReturned
.
Zmniejsz liczbę zapytań na ManyToManyField (problem n + 1)
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
Rozwiązanie
Użyj prefetch_related
na ManyToManyField
jeśli wiesz, że będziesz musiał później uzyskać dostęp do pola, które jest polem ManyToManyField
.
# 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
może być również używana w polach odnośników:
# 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
Jednak po wykonaniu zestawu zapytań pobranych danych nie można zmienić bez ponownego uderzenia w bazę danych. Następujące zapytania wykonują na przykład dodatkowe zapytania:
# 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)
Poniższe można zoptymalizować za pomocą obiektu Prefetch
, wprowadzonego w Django 1.7:
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
Zmniejsz liczbę zapytań w polu ForeignKey (problem n + 1)
Problem
Zestawy zapytań Django są oceniane w leniwy sposób. Na przykład:
# 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
Powyższy kod powoduje, że django wysyła zapytanie do bazy danych o autora każdej książki. Jest to nieefektywne i lepiej mieć tylko jedno zapytanie.
Rozwiązanie
Użyj select_related
na ForeignKey
jeśli wiesz, że będziesz musiał później uzyskać dostęp do pola 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
może być również używana w polach odnośników:
# 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
Pobierz zestaw zapytań SQL dla Django
Atrybut query
w zestawie zapytań zapewnia składnię SQL równoważną dla zapytania.
>>> queryset = MyModel.objects.all()
>>> print(queryset.query)
SELECT "myapp_mymodel"."id", ... FROM "myapp_mymodel"
Ostrzeżenie:
Dane wyjściowe powinny być wykorzystywane wyłącznie do celów debugowania. Wygenerowane zapytanie nie jest specyficzne dla backendu. W związku z tym parametry nie są poprawnie cytowane, co powoduje, że jest podatne na wstrzyknięcie SQL, a zapytanie może nawet nie być wykonywalne na zapleczu bazy danych.
Uzyskaj pierwszy i ostatni rekord z QuerySet
Aby zdobyć pierwszy obiekt:
MyModel.objects.first()
Aby uzyskać ostatnie obiekty:
MyModel.objects.last()
Korzystanie z filtru Pierwszy obiekt:
MyModel.objects.filter(name='simple').first()
Korzystanie z filtru Ostatni obiekt:
MyModel.objects.filter(name='simple').last()
Zaawansowane zapytania z obiektami F.
Obiekt F () reprezentuje wartość pola modelu lub kolumny z adnotacjami. Umożliwia odwoływanie się do wartości pól modelu i wykonywanie operacji na bazie danych przy użyciu ich bez faktycznego wyciągania ich z bazy danych do pamięci Python. - Wyrażenia F ()
Należy używać obiektów F()
ilekroć konieczne jest odwołanie się do wartości innego pola w zapytaniu. Same obiekty F()
nic nie znaczą i nie mogą i nie powinny być wywoływane poza zestawem zapytań. Służą do odwoływania się do wartości pola w tym samym zestawie zapytań.
Na przykład, biorąc pod uwagę model ...
SomeModel(models.Model):
...
some_field = models.IntegerField()
... użytkownik może wyszukiwać obiekty, których wartość some_field
jest dwa razy większa od id
, odwołując się do wartości pola id
podczas filtrowania za pomocą F()
następujący sposób:
SomeModel.objects.filter(some_field=F('id') * 2)
F('id')
po prostu odwołuje się do wartości id
dla tego samego wystąpienia. Django używa go do tworzenia odpowiedniej instrukcji SQL. W tym przypadku coś bardzo przypomina to:
SELECT * FROM some_app_some_model
WHERE some_field = ((id * 2))
Bez wyrażeń F()
można to osiągnąć za pomocą surowego SQL lub filtrowania w Pythonie (co zmniejsza wydajność, szczególnie gdy jest wiele obiektów).
Bibliografia:
Z definicji klasy F()
:
Obiekt zdolny do rozpoznawania odniesień do istniejących obiektów zapytania. - źródło F.
Uwaga: ten opublikowany przykład pochodzi z wyżej wymienionej odpowiedzi za zgodą TinyInstance.