Django
set di query
Ricerca…
introduzione
Un Queryset
è fondamentalmente un elenco di oggetti derivati da un Model
, da una raccolta di query di database.
Domande semplici su un modello standalone
Ecco un semplice modello che utilizzeremo per eseguire alcune query di test:
class MyModel(models.Model):
name = models.CharField(max_length=10)
model_num = models.IntegerField()
flag = models.NullBooleanField(default=False)
Ottieni un singolo oggetto modello in cui id / pk è 4:
(Se non ci sono elementi con l'id di 4 o ce ne sono più di uno, questo genererà un'eccezione).
MyModel.objects.get(pk=4)
Tutti gli oggetti del modello:
MyModel.objects.all()
Gli oggetti del modello con flag
impostato su True
:
MyModel.objects.filter(flag=True)
Oggetti del modello con un numero di model_num
maggiore di 25:
MyModel.objects.filter(model_num__gt=25)
Oggetti modello con il name
di "Articolo economico" e il flag
impostato su False
:
MyModel.objects.filter(name="Cheap Item", flag=False)
Modello semplice name
ricerca per stringa specifica (sensibile al maiuscolo / minuscolo):
MyModel.objects.filter(name__contains="ch")
Modello semplice name
ricerca per stringa specifica (senza distinzione tra maiuscole e minuscole):
MyModel.objects.filter(name__icontains="ch")
Query avanzate con oggetti Q
Dato il modello:
class MyModel(models.Model):
name = models.CharField(max_length=10)
model_num = models.IntegerField()
flag = models.NullBooleanField(default=False)
Possiamo utilizzare oggetti Q
per creare condizioni AND
, OR
nella query di ricerca. Ad esempio, supponiamo di volere tutti gli oggetti che hanno flag=True
OR model_num>15
.
from django.db.models import Q
MyModel.objects.filter(Q(flag=True) | Q(model_num__gt=15))
Quanto sopra si traduce in WHERE flag=True OR model_num > 15
modo simile per un AND che si dovrebbe fare.
MyModel.objects.filter(Q(flag=True) & Q(model_num__gt=15))
Q
oggetti Q
ci permettono anche di fare NOT query con l'uso di ~
. Diciamo che volevamo ottenere tutti gli oggetti che hanno flag=False
AND model_num!=15
, dovremmo fare:
MyModel.objects.filter(Q(flag=True) & ~Q(model_num=15))
Se si utilizzano oggetti Q e parametri "normali" in filter()
, gli oggetti Q
devono prima venire. La seguente query cerca i modelli con ( flag
impostato su True
o un numero di modello maggiore di 15
) e un nome che inizia con "H".
from django.db.models import Q
MyModel.objects.filter(Q(flag=True) | Q(model_num__gt=15), name__startswith="H")
Nota: gli oggetti Q
possono essere utilizzati con qualsiasi funzione di ricerca che accetta argomenti di parole chiave come filter
, exclude
, get
. Assicurati che quando usi con get
, restituirai un solo oggetto o verrà sollevata l'eccezione MultipleObjectsReturned
.
Riduci il numero di query su ManyToManyField (n + 1 problema)
Problema
# 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
Soluzione
Usa prefetch_related
su ManyToManyField
se sai che dovrai accedere più tardi a un campo che è un campo 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
può essere utilizzato anche nei campi di ricerca:
# 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
Tuttavia, una volta eseguito il queryset, i dati recuperati non possono essere modificati senza colpire nuovamente il database. Il seguente eseguirà query aggiuntive per esempio:
# 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)
Quanto segue può essere ottimizzato usando un oggetto Prefetch
, introdotto 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
Riduci il numero di query sul campo ForeignKey (n + 1 problema)
Problema
I querysets di Django sono valutati in modo pigro. Per esempio:
# 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
Il codice sopra fa in modo che django interroghi il database per l'autore di ogni libro. Questo è inefficiente ed è meglio avere una sola query.
Soluzione
Usa select_related
su ForeignKey
se sai che dovrai accedere successivamente a un campo 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
può essere utilizzato anche nei campi di ricerca:
# 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
Ottieni SQL per il queryset di Django
L'attributo query
su queryset fornisce una sintassi equivalente SQL per la query.
>>> queryset = MyModel.objects.all()
>>> print(queryset.query)
SELECT "myapp_mymodel"."id", ... FROM "myapp_mymodel"
Avvertimento:
Questo output dovrebbe essere utilizzato solo a scopo di debug. La query generata non è specifica per il back-end. Di conseguenza, i parametri non vengono citati correttamente, lasciandoli vulnerabili all'iniezione SQL e la query potrebbe non essere nemmeno eseguibile sul back-end del database.
Ottieni il primo e l'ultimo record da QuerySet
Per ottenere il primo oggetto:
MyModel.objects.first()
Per ottenere gli ultimi oggetti:
MyModel.objects.last()
Usando il filtro Primo oggetto:
MyModel.objects.filter(name='simple').first()
Utilizzo del filtro Ultimo oggetto:
MyModel.objects.filter(name='simple').last()
Query avanzate con oggetti F
Un oggetto F () rappresenta il valore di un campo modello o di una colonna annotata. Permette di fare riferimento ai valori dei campi del modello ed eseguire operazioni di database che li utilizzano senza doverli estrarre dal database in memoria Python. - F () espressioni
È opportuno utilizzare oggetti F()
ogni volta che è necessario fare riferimento al valore di un altro campo nella query. Di per sé, gli oggetti F()
non significano nulla, e non possono e non devono essere chiamati al di fuori di un queryset. Sono usati per fare riferimento al valore di un campo sullo stesso queryset.
Ad esempio, dato un modello ...
SomeModel(models.Model):
...
some_field = models.IntegerField()
... un utente può interrogare oggetti in cui il valore some_field
è il doppio del suo id
facendo riferimento al valore del campo id
mentre si filtra usando F()
come questo:
SomeModel.objects.filter(some_field=F('id') * 2)
F('id')
semplicemente riferimento al valore id
per quella stessa istanza. Django lo usa per creare un'istruzione SQL corrispondente. In questo caso qualcosa di simile a questo:
SELECT * FROM some_app_some_model
WHERE some_field = ((id * 2))
Senza le espressioni F()
ciò sarebbe possibile con SQL raw o filtro in Python (che riduce le prestazioni soprattutto quando ci sono molti oggetti).
Riferimenti:
Dalla definizione della classe F()
:
Un oggetto in grado di risolvere i riferimenti a oggetti di query esistenti. - F fonte
Nota: questo esempio pubblicato proviene dalla risposta sopra elencata con il consenso di TinyInstance.