Все мы знаем, что Django предоставляет ORM прямо из коробки, что не позволяет нам писать SQL-запросы. В этом механизме есть большой потенциал. Однако большинство из нас знакомы только с методами filter()
, get()
, update()
и delete()
из Django ORM.
В Django ORM есть много других опций, поэтому у нас все же возможность использовать функционал традиционных SQL-запросов. Сегодня в статье я вам про него расскажу.
exclude()
values()
values_list()
select_related()
order_by()
exists()
count()
first() and last()
in_bulk()
explain()
latest()
earliest()
Я воспользуюсь таблицей Student
, чтобы показать функционал вышеуказанных методов. Вот так выглядит класс Student
из файла models.py.
class Student(models.Model):
name = models.CharField(max_length=100)
grade = models.IntegerField()
section = models.CharField(max_length=10)
school = models.ForeignKey(School, on_delete=models.CASCADE)
blood_group = models.CharField(max_length=10)
mobile = models.CharField(max_length=20)
address = models.TextField() def __str__(self):
return self.name
У таблицы Student
есть внешний ключ, который связывает ее с таблицей School
.
class School(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField(null=True, blank=True)
address = models.TextField() def __str__(self):
return self.name
Итак, начнем.
1. exclude()
Первый метод - exclude()
. Он возвращает нам набор запросов без заданного вами значения. В таблице Student
у меня есть 4 студента. Для начала, получим их через all()
.
>>> queryset = Student.objects.all()
>>> queryset
<QuerySet [<Student: Regina Johnson>, <Student: Eva Smith>, <Student: Jessie Smith>, <Student: John David>]>
Мне не нужно, что Eva Smith присутствовала в выдаче. Поэтому я вызову метод exclude()
с ее именем на модели Student
.
>>> queryset = Student.objects.exclude(name='Eva Smith')
>>> queryset
<QuerySet [<Student: Regina Johnson>, <Student: Jessie Smith>, <Student: John David>]>
Как видите, теперь мы не получаем ее имя в запросе.
2. Values()
Следующий метод - метод values()
. Он возвращает словари Python вместо объекта QuerySet
.
>>> Student.objects.values()<QuerySet [{'id': 1, 'name': 'Regina Johnson', 'grade': 10, 'section': 'A', 'school_id': 1, 'blood_group': 'A+', 'mobile': '9791684645', 'address': '93 Jessica Ln, Depew, NY, 14043'}, {'id': 3, 'name': 'Eva Smith', 'grade': 12, 'section': 'A', 'school_id': 1, 'blood_group': 'A1+', 'mobile': '8907896543', 'address': '2012 Walnut Ave #J, Ceres, CA, 95307'}, {'id': 4, 'name': 'Jessie Smith', 'grade': 12, 'section': 'A', 'school_id': 1, 'blood_group': 'A1+', 'mobile': '8907896543', 'address': '503 Courtney Dr, Brusly, LA, 70719'}, {'id': 5, 'name': 'John David', 'grade': 12, 'section': 'A', 'school_id': 1, 'blood_group': 'A1+', 'mobile': '2675431231', 'address': '34 Leaman Pl, Lynbrook, NY, 11563'}]>
Еще мы можем извлекать только те поля, которые нам требуются, передавая их имена в качестве аргументов методу values()
. Допустим, мне нужны только id
и name
студентов. Я могу сделать так.
>>> Student.objects.values('id', 'name')<QuerySet [{'id': 1, 'name': 'Regina Johnson'}, {'id': 3, 'name': 'Eva Smith'}, {'id': 4, 'name': 'Jessie Smith'}, {'id': 5, 'name': 'John David'}]>
3. values_list()
Метод values_list()
схож с методом values()
, но вместо словарей он возвращает кортежи.
>>> Student.objects.values_list('id', 'name')<QuerySet [(1, 'Regina Johnson'), (3, 'Eva Smith'), (4, 'Jessie Smith'), (5, 'John David')]>
Если нам нужно только одно значение, например список, вместо кортежа, мы можем передать методу values_list()
именованный аргумент flat=True
. Если нужно только поле name
в виде списка, я могу сделать следующим образом.
>>> Student.objects.values_list('name', flat=True)<QuerySet ['Regina Johnson', 'Eva Smith', 'Jessie Smith', 'John David']>
Примечание: Метод будет работать только с одним полем. Если вы укажете больше - получите сообщение об ошибке.
>>> Student.objects.values_list('id', 'name', flat=True)
TypeError: 'flat' is not valid when values_list is called with more than one field.
4. select_related()
Этот метод мне очень нравится. Как я уже говорил, моя таблица Student
связана внешним ключом с таблицей School
. Чтобы узнать в какой школе учится студент, я могу сделать следующий запрос.
>>> student = Student.objects.get(pk=1)
>>> student.school
<School: Montfort>
Чтобы получить конкретного студента, запрос с его id
отправляется таблице Student
. Затем, чтобы получить школу, мы выполняем дополнительный поиск в базе данных.
Примечание: Для каждой связи по внешнему ключу требуется дополнительный проход по базе данных.
В случае с нашим простым примером это не проблема, но в больших базах данных со большим количеством связей нагрузка на базу данных может быть непомерно высокой.
Мы можем использовать select_related()
для повышения производительности базы данных путем извлечения всех связанных данных при первом обращении к ней.
>>> student = Student.objects.select_related('school').get(pk=1)
>>> student.school # school has already been retrieved. Database is not hit again.
<School: Montfort>
В этом случае данные про школу извлекаются из предварительно выбранных данных при выполнении первого запроса вместо повторных запросов.
5. order_by()
Метод order_by()
изменяет порядок выдачи запросов по умолчанию. По умолчанию запросы выдаются с сортировкой по первичным ключам (id). Если я хочу, чтобы мой QuerySet упорядочивался по имени, я могу передать поле name
методу order_by()
.
Если я захочу, чтобы мой QuerySet упорядочивался по имени в порядке возрастания, я могу сделать что-то вроде этого.
>>> Student.objects.order_by('name')<QuerySet [<Student: Eva Smith>, <Student: Jessie Smith>, <Student: John David>, <Student: Regina Johnson>]>
А если в порядке убывания:
>>> Student.objects.order_by('-name')<QuerySet [<Student: Regina Johnson>, <Student: John David>, <Student: Jessie Smith>, <Student: Eva Smith>]>
Отрицательное значение поля name
– тоже рабочий трюк.
6. exists()
Метод exists()
возвращает True
, если возвращаемый QuerySet содержит один или несколько объектов, и значение False
, если QuerySet пустой.
>>> Student.objects.filter(name='Regina Johnson').exists()
True
>>> Student.objects.filter(name='Regina David').exists()
False
В моей базе данных есть студентка по имени Regina Johnson, и поэтому метод exists()
возвращает True
при вызове с параметром «Regina Johnson» и False
в других случаях.
7. count()
Метод count()
считает количество записей в QuerySet. Его можно использовать для подсчета всех объектов в таблице базы данных.
>>> Student.objects.count()
4
Или для подсчета количества объектов, возвращаемых запросом:
>>> Student.objects.filter(name='Regina Johnson').count()
1
Метод count()
функционально эквивалентен методу aggregate()
, но у него более чистый синтаксис и он, вероятно, будет быстрее работать с большими объемами данных.
8. first() and last()
Метод first()
возвращает первый элемент из QuerySet.
>>> Student.objects.all()
<QuerySet [<Student: Regina Johnson>, <Student: Eva Smith>, <Student: Jessie Smith>, <Student: John David>]>>>> Student.objects.all().first()
<Student: Regina Johnson>
Метод last()
возвращает последний элемент из QuerySet.
>>> Student.objects.all()
<QuerySet [<Student: Regina Johnson>, <Student: Eva Smith>, <Student: Jessie Smith>, <Student: John David>]>>>> Student.objects.all().last()
<Student: John David>
Несмотря на то, что QuerySet похож на список, и вы можете получить первый элемент, по индексу вида queryset[0]
, но получить последний элемент через queryset[-1]
не получится. Вы получите сообщение об ошибке. В этом случае метод last()
придется очень кстати.
>>> Student.objects.all()[0]
<Student: Regina Johnson>
>>> Student.objects.all()[-1] "Negative indexing is not supported."
AssertionError: Negative indexing is not supported.
9. in_bulk()
in_bulk()
принимает список значений id
и возвращает словарь, где каждый id
сопоставляется с экземпляром объекта. Если вы не передадите список методу in_bulk()
, то получите все объекты.
Допустим, я хочу получить только студентов с id 1 и 4, я могу сделать так.
>>> students = Student.objects.in_bulk([1, 4])
>>> students[1].name
'Regina Johnson'
>>> students[4].name
'Jessie Smith'
Затем я могу получить доступ к объекту, используя id
в качестве индекса.
10. explain()
Этот метод возвращает строку плана выполнения QuerySet. Используется для анализа производительности запросов.
>>> Student.objects.filter(pk=1).explain()
'2 0 0 SEARCH TABLE student_student USING INTEGER PRIMARY KEY (rowid=?)'
11. latest()
Метод latest()
аналогичен методу last()
. Он возвращает последний объект в таблице на основе заданных полей.
Например, если у вас есть модель блога, и вы хотите получить последнюю измененную запись, вы можете сделать так.
>>> Blog.objects.latest('updated_at')
<Blog: Blog object (17)>
Что эквивалентно следующей конструкции:
>>> Blog.objects.all().order_by('updated_at').last()
<Blog: Blog object (17)>
Метод latest()
также может принимать несколько имен полей.
>>> Blog.objects.latest('updated_at', '-created_at')
<Blog: Blog object (17)>
Примечание: Если вы установили порядок мета-данных в своей модели, результаты могут отличаться.
12. earliest()
Метод earliest()
аналогичен методу first()
и противоположен методу latest()
.
Он принимает несколько имен полей и возвращает первую запись из таблицы.
>>> Blog.objects.earliest('updated_at')
<Blog: Blog object (17)>
Надеюсь, эта статья окажется полезной. Удачного кода!
Источники: https://docs.djangoproject.com/en/3.2/ref/models/querysets/
Всех заинтересованных приглашаем на открытый урок «Тестирование в Django». На занятии мы научимся писать тесты для проекта на Django: Сначала мы напишем тесты с помощью библиотеки requests, а затем и с помощью Django-тестов.
Комментарии (4)
Pavel1114
03.03.2022 04:40Зачем? Информации меньше чем в документации. Хотя оттуда можно было что нибудь более интересное нарыть (типа комбинаций values и annotation или использование оконных функций).
По умолчанию запросы выдаются с сортировкой по первичным ключам (id)
Неверно. Если не указывать order ни в Meta модели ни в Queryset, то порядок не гарантируется.If a query doesn’t have an ordering specified, results are returned from the database in an unspecified order
Deq56
03.03.2022 06:20Это самые часто применяемые методы + prefetch_related забыли. Не часто это всякие anotate + subquery
ovalsky
а я вижу)
ovalsky
исправьте пример на это: