Сортировка, изменение и удаление записей

Курс по Django: https://stepik.org/a/183363

Архив проекта: 21_sitewomen.zip

Продолжаем знакомиться с базовыми возможностями ORM Django. Снова перейдем в консоль shell_plus:

python manage.py shell_plus --print-sql

(в оболочке shell_plus модели импортируются автоматически).

На данный момент мы с вами научились создавать, добавлять новые записи в таблицу БД, а также выбирать одну или несколько записей по определенному критерию. Однако довольно часто бывает необходимость, при выборке дополнительно выполнять сортировки записей по указанному полю. Для этого используется метод order_by(), например, следующим образом:

Women.objects.all().order_by('title')

Получаем SQL-запрос, в котором появляется новое ключевое слово ORDER BY с флагом ASC (сортировка по возрастанию). На выходе получим список:

<QuerySet [<Women: Анджелина Джоли>, <Women: Джулия Робертс>, <Women: Екатерина Гусева>, <Women: Кира Найтли>, <Women: Ума Турман>, <Women: Энн Хэтэуэй>]>

Или, ту же самую команду можно записать в виде:

Women.objects.order_by('title')

Результат будет абсолютно тем же самым.

Вообще, метод order_by() можно применять к любой коллекции QuerySet. Например, с помощью фильтра отобрать несколько записей, а затем, выполнить их фильтрацию:

Women.objects.filter(pk__lte=4).order_by('title')

Здесь отбираются все записи, у которых id меньше или равен 4 и сортируются по полю title в порядке возрастания (используется лексикографическое сравнение строк). Этот пример показывает, как методы можно цепочкой выполнять друг за другом: сначала выбрали записи по условию, а затем их отсортировали. Точно также по цепочке можно выполнять многие другие методы ORM.

Если нам нужно изменить порядок сортировки на противоположный, то перед именем поля достаточно поставить знак минус:

Women.objects.order_by('-time_update')

В этом случае в SQL-запросе после ключевого слова ORDER BY появляется флаг DESC, означающий сортировку по убыванию.

Вложенный класс Meta

При необходимости, в любой модели мы можем определить некоторые глобальные настройки. Например, по умолчанию выполнять сортировку статей по убыванию времени их создания. Для этого внутри класса модели следует прописать еще один класс Meta и в нем определить атрибут ordering следующим образом:

class Women(models.Model):
    ...
    class Meta:
        ordering = ['-time_create']
        indexes = [
            models.Index(fields=['-time_create']),
        ]
 
    def __str__(self):
        return self.title

И дополнительно здесь сразу указано, что это поле должно быть индексируемым, чтобы сортировка выполнялась быстрее. Вообще у этого класса множество разных атрибутов. Подробнее о них можно посмотреть на следующей странице документации:

https://docs.djangoproject.com/en/4.2/ref/models/options/

Если теперь снова зайти в консоль shell_plus:      

python manage.py shell_plus --print-sql

то при извлечении всех записей:

Women.objects.all()

они будут следовать в обратном порядке согласно атрибуту ordering класса Meta.

Изменение записей

Следующим шагом посмотрим, как можно с помощью команд ORM Django менять данные в уже существующих записях таблицы БД. В самом простом случае, для изменения какой-либо записи, ее можно сначала прочитать из БД, например, с помощью метода get():

wu = Women.objects.get(pk=2)

Затем, присвоить атрибутам объекта Women другие значения, например:

wu.title = 'Марго Робби'
wu.content = 'Биография Марго Робби'

Сохраняем новые данные:

wu.save()

и в таблице видим, что вторая запись содержит новую, измененную информацию. При этом последний SQL-запрос имеет следующий вид:

'UPDATE "women_women" SET "title" = \'Марго Робби\', "content" = \'Биография Марго Робби\', "photo" = \'\', "time_create" = \'2021-01-03 09:27:56.511898\', "time_update" = \'2021-01-03 11:57:06.800768\', "is_published" = 1 WHERE "women_women"."id" = 2'

Это первый подход к изменению записей, когда у нас уже имеется объект, который нужно изменить. Но что если нам нужно, например, у всех записей поле is_published установить в ноль? Как это сделать? В этом случае нам поможет специальный метод update(), который можно использовать следующим образом:

Women.objects.update(is_published=0)

В итоге, поле is_published для всех записей будет установлено в ноль. Или, можно поменять это же значение только для первых четырех записей, допустим, установить в единицу. Если попробовать это сделать через срезы:

Women.objects.all()[:4].update(is_published=1)

то получим ошибку. Метод update() нельзя комбинировать со срезами. Но после метода filter() его можно вызывать. Поэтому следующая команда даст нужный нам результат:

Women.objects.filter(pk__lte=4).update(is_published=1)

Также получим ошибку, если метод update() вызвать для объекта класса Women, например, так:

Women.objects.get(pk=5).update(is_published=1)

И это понятно, так как для изменения записей в SQL-запросах на первом месте должно стоять ключевое слово UPDATE, а при формировании выборки – ключевое слово SELECT. То есть, это два разных типа запросов и комбинировать их нельзя. Отсюда и получаем ошибку их выполнения.

Удаление записей

Наконец, последняя базовая операция – удаление записей, выполняется аналогично изменению. То есть, сначала нужно выбрать те записи, что мы собираемся удалить, например, вот так:

wd = Women.objects.filter(pk__gte=5)

А, затем, выполняем для них метод delete():

wd.delete()

Все, записи с id равным 5 и 6 были удалены из таблицы women.

На этом занятии мы лишь в целом рассмотрели некоторые возможности ORM Django. Более детальную информацию можно почитать на странице документации:

docs.djangoproject.com/en/4.2/topics/db/queries/

Курс по Django: https://stepik.org/a/183363

Видео по теме