Поиск…


Вступление

Queryset - это в основном список объектов, полученных из Model , путем компиляции запросов к базе данных.

Простые запросы на автономную модель

Вот простая модель, которую мы будем использовать для запуска нескольких тестовых запросов:

class MyModel(models.Model):
    name = models.CharField(max_length=10)
    model_num = models.IntegerField()
    flag = models.NullBooleanField(default=False)

Получить один объект модели, где id / pk равно 4:
(Если нет элементов с идентификатором 4 или их более одного, это вызовет исключение).

MyModel.objects.get(pk=4)

Все объекты модели:

MyModel.objects.all()

Объекты модели, для которых установлен flag True :

MyModel.objects.filter(flag=True)

Объекты модели с model_num больше 25:

MyModel.objects.filter(model_num__gt=25)

Объекты модели с name «Дешевый элемент» и flag установленный в значение « False :

MyModel.objects.filter(name="Cheap Item", flag=False)

Моделирует простое name поиска для конкретной строки (с учетом регистра):

MyModel.objects.filter(name__contains="ch")

Модели простое name поиска для конкретной строки (нечувствительность к регистру):

MyModel.objects.filter(name__icontains="ch")

Расширенные запросы с объектами Q

Учитывая модель:

class MyModel(models.Model):
    name = models.CharField(max_length=10)
    model_num = models.IntegerField()
    flag = models.NullBooleanField(default=False)

Мы можем использовать объекты Q для создания условий AND , OR в вашем поисковом запросе. Например, предположим, что мы хотим, чтобы все объекты имели flag=True OR model_num>15 .

from django.db.models import Q
MyModel.objects.filter(Q(flag=True) | Q(model_num__gt=15))

Вышеприведенное WHERE flag=True OR model_num > 15 аналогично для AND, которое вы бы сделали.

MyModel.objects.filter(Q(flag=True) & Q(model_num__gt=15))

Объекты Q также позволяют нам делать НЕ- запросы с использованием ~ . Предположим, мы хотели получить все объекты с flag=False AND model_num!=15 , мы бы сделали:

MyModel.objects.filter(Q(flag=True) & ~Q(model_num=15)) 

При использовании объектов Q и «нормальных» параметров в filter() , объекты Q должны быть первыми . Следующий запрос выполняет поиск моделей с ( flag установлен на True или номер модели больше 15 ) и имя, которое начинается с «H».

from django.db.models import Q
MyModel.objects.filter(Q(flag=True) | Q(model_num__gt=15), name__startswith="H")

Примечание. Объекты Q могут использоваться с любой функцией поиска, которая принимает аргументы ключевого слова, такие как filter , exclude , get . Убедитесь в том , что при использовании с get , что вы будете возвращать только один объект или MultipleObjectsReturned будет сгенерировано исключение.

Уменьшите количество запросов на ManyToManyField (проблема n + 1)

проблема

# 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

Решение

Используйте prefetch_related на ManyToManyField если вы знаете, что вам нужно будет получить доступ к полю, которое является полем 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 также может использоваться для полей поиска:

# 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

Однако, как только запрос будет выполнен, данные не могут быть изменены без повторного использования базы данных. Следующие могут выполнять дополнительные запросы, например:

 # 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)

Следующие могут быть оптимизированы с использованием объекта Prefetch , введенного в 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

Уменьшить количество запросов в поле ForeignKey (n + 1 выпуск)

проблема

Запросы Django оцениваются ленивым способом. Например:

# 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

Приведенный выше код заставляет django запрашивать базу данных для автора каждой книги. Это неэффективно, и лучше иметь только один запрос.

Решение

Используйте select_related on ForeignKey если вы знаете, что вам нужно будет позже получить доступ к полю 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 также может использоваться для полей поиска:

# 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

Получить SQL для набора запросов Django

Атрибут query дает синтаксис SQL-эквивалента для вашего запроса.

>>> queryset = MyModel.objects.all()
>>> print(queryset.query)
SELECT "myapp_mymodel"."id", ... FROM "myapp_mymodel"

Предупреждение:

Этот вывод следует использовать только для целей отладки. Сгенерированный запрос не зависит от сервера. Таким образом, параметры не котируются должным образом, что делает его уязвимым для SQL-инъекции, и запрос может даже не выполняться в бэкэнд базы данных.

Получить первую и последнюю запись из QuerySet

Чтобы получить первый объект:

MyModel.objects.first()

Чтобы получить последние объекты:

MyModel.objects.last()

Использование объекта Filter First:

MyModel.objects.filter(name='simple').first()

Использование фильтра Последний объект:

MyModel.objects.filter(name='simple').last()

Расширенные запросы с объектами F

Объект F () представляет значение поля модели или аннотированного столбца. Это позволяет ссылаться на значения полей модели и выполнять операции с ними, не выбирая их из базы данных в память Python. - выражения F ()

Уместно использовать объекты F() когда вам нужно ссылаться на значение другого поля в вашем запросе. Само по себе объекты F() ничего не значат, и их нельзя и не следует вызывать вне набора запросов. Они используются для ссылки на значение поля в том же наборе запросов.

Например, учитывая модель ...

SomeModel(models.Model):
    ...
    some_field = models.IntegerField()

... пользователь может запрашивать объекты, где значение some_field в два раза превышает его id , ссылаясь на значение поля id при фильтрации с помощью F() следующим образом:

SomeModel.objects.filter(some_field=F('id') * 2)

F('id') просто ссылается на значение id для этого же экземпляра. Django использует его для создания соответствующего оператора SQL. В этом случае что-то очень похожее на это:

SELECT * FROM some_app_some_model 
WHERE some_field = ((id * 2))

Без выражений F() это было бы достигнуто либо с помощью исходного SQL, либо с помощью фильтрации в Python (что снижает производительность, особенно когда есть много объектов).


Рекомендации:

Из определения класса F() :

Объект, способный разрешать ссылки на существующие объекты запросов. - источник F

Примечание. Этот пример отправлен из ответа, указанного выше, с согласия TinyInstance.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow