Django
QuerySets
Zoeken…
Invoering
Een Queryset
is in wezen een lijst met objecten afgeleid van een Model
, door een compilatie van databasequery's.
Eenvoudige zoekopdrachten op een zelfstandig model
Hier is een eenvoudig model dat we zullen gebruiken om een paar testquery's uit te voeren:
class MyModel(models.Model):
name = models.CharField(max_length=10)
model_num = models.IntegerField()
flag = models.NullBooleanField(default=False)
Verkrijg een enkel modelobject met een id / pk van 4:
(Als er geen items met de id 4 zijn of er meerdere zijn, geeft dit een uitzondering.)
MyModel.objects.get(pk=4)
Alle modelobjecten:
MyModel.objects.all()
Modelobjecten waarvoor flag
ingesteld op True
:
MyModel.objects.filter(flag=True)
model_num
met een model_num
groter dan 25:
MyModel.objects.filter(model_num__gt=25)
Modelobjecten met de name
"Goedkoop artikel" en flag
ingesteld op False
:
MyModel.objects.filter(name="Cheap Item", flag=False)
Modellen eenvoudig zoeken name
voor specifieke string (hoofdlettergevoelig):
MyModel.objects.filter(name__contains="ch")
Modellen eenvoudig zoeken name
voor specifieke string (Case-insensitive):
MyModel.objects.filter(name__icontains="ch")
Geavanceerde zoekopdrachten met Q-objecten
Gezien het model:
class MyModel(models.Model):
name = models.CharField(max_length=10)
model_num = models.IntegerField()
flag = models.NullBooleanField(default=False)
We kunnen Q
objecten gebruiken om AND
of OR
voorwaarden te maken in uw opzoekquery. Stel bijvoorbeeld dat we alle objecten met flag=True
OF model_num>15
.
from django.db.models import Q
MyModel.objects.filter(Q(flag=True) | Q(model_num__gt=15))
Het bovenstaande vertaalt zich in WHERE flag=True OR model_num > 15
dezelfde manier voor een AND die u zou doen.
MyModel.objects.filter(Q(flag=True) & Q(model_num__gt=15))
Q
objecten kunnen we ook GEEN zoekopdrachten uitvoeren met behulp van ~
. Laten we zeggen dat we alle objecten met flag=False
EN model_num!=15
wilden krijgen, zouden we doen:
MyModel.objects.filter(Q(flag=True) & ~Q(model_num=15))
Bij gebruik Q objecten "normale" parameters filter()
, wordt de Q
objecten eerst moet komen. De volgende zoekopdracht zoekt naar modellen met ( flag
ingesteld op True
of een modelnummer groter dan 15
) en een naam die begint met "H".
from django.db.models import Q
MyModel.objects.filter(Q(flag=True) | Q(model_num__gt=15), name__startswith="H")
Opmerking: Q
objecten kunnen worden gebruikt met elke opzoekfunctie waarvoor trefwoordargumenten nodig zijn, zoals filter
, exclude
, get
. Zorg ervoor dat wanneer u met get
, u slechts één object retourneert of dat de uitzondering MultipleObjectsReturned
wordt verhoogd.
Verminder het aantal zoekopdrachten op ManyToManyField (probleem n + 1)
Probleem
# 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
Oplossing
Gebruik prefetch_related
op ManyToManyField
als u weet dat u later toegang moet krijgen tot een veld dat een ManyToManyField
veld is.
# 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 ook worden gebruikt in opzoekvelden:
# 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
Zodra de queryset is uitgevoerd, kunnen de opgehaalde gegevens echter niet meer worden gewijzigd zonder opnieuw de database te raken. Het volgende zou bijvoorbeeld extra zoekopdrachten uitvoeren:
# 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)
Het volgende kan worden geoptimaliseerd met een Prefetch
object, geïntroduceerd in 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
Verminder het aantal zoekopdrachten in het ForeignKey-veld (probleem n + 1)
Probleem
Django-querysets worden op een luie manier geëvalueerd. Bijvoorbeeld:
# 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
De bovenstaande code zorgt ervoor dat django de database doorzoekt voor de auteur van elk boek. Dit is inefficiënt en het is beter om slechts één query te hebben.
Oplossing
Gebruik select_related
op ForeignKey
als u weet dat u later toegang moet krijgen tot een ForeignKey
veld.
# 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 ook worden gebruikt in opzoekvelden:
# 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
Download SQL voor Django queryset
De query
attribuut op queryset geeft u SQL-equivalent syntaxis voor uw zoekopdracht.
>>> queryset = MyModel.objects.all()
>>> print(queryset.query)
SELECT "myapp_mymodel"."id", ... FROM "myapp_mymodel"
Waarschuwing:
Deze uitvoer mag alleen worden gebruikt voor foutopsporing. De gegenereerde zoekopdracht is niet backend-specifiek. Als zodanig worden de parameters niet correct geciteerd, waardoor het kwetsbaar is voor SQL-injectie en de query mogelijk niet eens uitvoerbaar is in uw database-backend.
Ontvang het eerste en laatste record van QuerySet
Om het eerste object te krijgen:
MyModel.objects.first()
Laatste objecten ophalen:
MyModel.objects.last()
Filter First-object gebruiken:
MyModel.objects.filter(name='simple').first()
Filter Laatste object gebruiken:
MyModel.objects.filter(name='simple').last()
Geavanceerde zoekopdrachten met F-objecten
Een F () -object vertegenwoordigt de waarde van een modelveld of geannoteerde kolom. Het maakt het mogelijk om te verwijzen naar modelveldwaarden en databasebewerkingen uit te voeren zonder ze daadwerkelijk uit de database naar Python-geheugen te hoeven halen. - F () uitdrukkingen
Het is aangewezen om F()
-objecten te gebruiken wanneer u in uw zoekopdracht naar de waarde van een ander veld moet verwijzen. Op zichzelf betekenen objecten F()
niets en kunnen en mogen ze niet buiten een queryset worden aangeroepen. Ze worden gebruikt om naar de waarde van een veld op dezelfde queryset te verwijzen.
Bijvoorbeeld, gegeven een model ...
SomeModel(models.Model):
...
some_field = models.IntegerField()
... een gebruiker kan objecten opvragen waarbij de waarde some_field
twee keer zijn id
door naar de waarde van het id
veld te verwijzen terwijl hij met F()
als volgt filtert:
SomeModel.objects.filter(some_field=F('id') * 2)
F('id')
verwijst eenvoudigweg naar de id
waarde voor diezelfde instantie. Django gebruikt het om een overeenkomstige SQL-instructie te maken. In dit geval lijkt dit sterk op dit:
SELECT * FROM some_app_some_model
WHERE some_field = ((id * 2))
Zonder F()
-uitdrukkingen zou dit worden bereikt met ofwel ruwe SQL of filtering in Python (wat de prestaties vermindert, vooral wanneer er veel objecten zijn).
Referenties:
Uit de definitie van de klasse F()
:
Een object dat verwijzingen naar bestaande queryobjecten kan oplossen. - F bron
Opmerking: dit gepubliceerde voorbeeld is afkomstig van het bovenstaande antwoord met toestemming van TinyInstance.