Все мы знаем, что 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)


  1. ovalsky
    02.03.2022 15:34
    +1

    Как видите, теперь мы не получаем ее имя в запросе.

    а я вижу)


    1. ovalsky
      02.03.2022 15:57

      исправьте пример на это:

      >>> queryset = Student.objects.exclude(name='Eva Smith')>>> queryset<QuerySet [<Student: Regina Johnson>, <Student: Jessie Smith>, <Student: John David>]>


  1. 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


  1. Deq56
    03.03.2022 06:20

    Это самые часто применяемые методы + prefetch_related забыли. Не часто это всякие anotate + subquery