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.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow