Django
Querysets
Sök…
Introduktion
En Queryset
är i grunden en lista med objekt som härrör från en Model
, genom en sammanställning av databasfrågor.
Enkla frågor på en fristående modell
Här är en enkel modell som vi kommer att använda för att köra några testfrågor:
class MyModel(models.Model):
name = models.CharField(max_length=10)
model_num = models.IntegerField()
flag = models.NullBooleanField(default=False)
Skaffa ett enda modellobjekt där id / pk är 4:
(Om det inte finns några artiklar med ID: n eller om det finns fler än ett, kommer detta att göra ett undantag.)
MyModel.objects.get(pk=4)
Alla modellobjekt:
MyModel.objects.all()
Modellobjekt som har flag
inställd på True
:
MyModel.objects.filter(flag=True)
model_num
med model_num
större än 25:
MyModel.objects.filter(model_num__gt=25)
Modellobjekt med name
"Billigt objekt" och flag
inställt på False
:
MyModel.objects.filter(name="Cheap Item", flag=False)
Modeller enkel sökning name
för specifik sträng (skiftlägeskänsligt):
MyModel.objects.filter(name__contains="ch")
Modeller enkel sökning name
för specifik sträng (Case-okänslig):
MyModel.objects.filter(name__icontains="ch")
Avancerade frågor med Q-objekt
Med tanke på modellen:
class MyModel(models.Model):
name = models.CharField(max_length=10)
model_num = models.IntegerField()
flag = models.NullBooleanField(default=False)
Vi kan använda Q
objekt för att skapa AND
, OR
villkor i din sökfråga. Till exempel, säg att vi vill ha alla objekt som har flag=True
ELLER model_num>15
.
from django.db.models import Q
MyModel.objects.filter(Q(flag=True) | Q(model_num__gt=15))
Ovanstående översätter till WHERE flag=True OR model_num > 15
liknande sätt för en OCH du skulle göra.
MyModel.objects.filter(Q(flag=True) & Q(model_num__gt=15))
Q
objekt tillåter oss också att INTE göra frågor med ~
. Låt oss säga att vi ville få alla objekt som har flag=False
OCH model_num!=15
, vi skulle göra:
MyModel.objects.filter(Q(flag=True) & ~Q(model_num=15))
Om du använder Q-objekt och "normala" parametrar i filter()
, måste Q
objekten komma först . Följande fråga söker efter modeller med ( flag
inställd på True
eller ett modellnummer större än 15
) och ett namn som börjar med "H".
from django.db.models import Q
MyModel.objects.filter(Q(flag=True) | Q(model_num__gt=15), name__startswith="H")
Obs: Q
objekt kan användas med alla uppslagningsfunktioner som tar nyckelordargument som filter
, exclude
, get
. Se till att när du använder med get
att du bara returnerar ett objekt eller att MultipleObjectsReturned
undantaget kommer att höjas.
Minska antalet frågor på ManyToManyField (n + 1 nummer)
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ösning
Använd prefetch_related
på ManyToManyField
om du vet att du senare måste komma åt ett fält som är ett ManyToManyField
fält.
# 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
kan också användas i uppslagningsfält:
# 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
När emellertid queryset har utförts kan data som hämtas dock inte ändras utan att träffa databasen igen. Följande skulle utföra extra frågor till exempel:
# 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)
Följande kan optimeras med ett Prefetch
objekt, introducerat i 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
Minska antalet frågor på fältet ForeignKey (n + 1 nummer)
Problem
Django-frågeställningar utvärderas på ett lat sätt. Till exempel:
# 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
Koden ovan får django att fråga databasen för författaren till varje bok. Detta är ineffektivt, och det är bättre att bara ha en enda fråga.
Lösning
Använd select_related
på ForeignKey
om du vet att du senare måste komma åt ett ForeignKey
fält.
# 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
kan också användas i sökfält:
# 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
Skaffa SQL för Django-queryset
Den query
attribut på queryset ger SQL motsvarande syntax för din fråga.
>>> queryset = MyModel.objects.all()
>>> print(queryset.query)
SELECT "myapp_mymodel"."id", ... FROM "myapp_mymodel"
Varning:
Denna utgång ska endast användas för felsökning. Den genererade frågan är inte backendspecifik. Som sådana citeras inte parametrarna ordentligt, vilket lämnar den sårbar för SQL-injektion, och frågan kanske inte ens är körbar i din databasbackend.
Få första och sista posten från QuerySet
För att få första objektet:
MyModel.objects.first()
För att få sista föremål:
MyModel.objects.last()
Använda filterets första objekt:
MyModel.objects.filter(name='simple').first()
Använda filter förra objektet:
MyModel.objects.filter(name='simple').last()
Avancerade frågor med F-objekt
Ett F () -objekt representerar värdet på ett modellfält eller en kommenterad kolumn. Det gör det möjligt att hänvisa till modellfältvärden och utföra databasåtgärder med hjälp av dem utan att behöva dra dem ut ur databasen i Python-minnet. - F () uttryck
Det är lämpligt att använda F()
-objekt när du behöver referera till ett annat fältvärde i din fråga. I sig själv betyder inte F()
-objekt något, och de kan inte och bör inte kallas utanför en frågeställning. De används för att referera till ett fältvärde på samma frågeställning.
Till exempel, med en modell ...
SomeModel(models.Model):
...
some_field = models.IntegerField()
... en användare kan fråga objekt där some_field
är två gånger dess id
genom att hänvisa till id
fältets värde medan du filtrerar med F()
så här:
SomeModel.objects.filter(some_field=F('id') * 2)
F('id')
hänvisar helt enkelt till id
värdet för samma instans. Django använder den för att skapa motsvarande SQL-sats. I detta fall något som liknar detta:
SELECT * FROM some_app_some_model
WHERE some_field = ((id * 2))
Utan F()
-uttryck skulle detta uppnås med antingen rå SQL eller filtrering i Python (vilket minskar prestandan särskilt när det finns massor av objekt).
referenser:
Från F()
klassdefinition:
Ett objekt som kan lösa referenser till befintliga frågeställningar. - F-källa
Obs: Detta publicerade exempel kom från svaret som listas ovan med samtycke från TinyInstance.