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.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow