Django
Querysets
Поиск…
Вступление
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.