Одна из задач, которую берут на себя ORM – это построение произвольных SQL-запросов в базу данных. Вот как это делается с помощью Django ORM:
# SELECT * FROM users WHERE name = "John" ORDER BY last_name DESC LIMIT 10
users = User.objects.filter( # WHERE
first_name='John',
).order_by( # ORDER BY
'-last_name'
)[:10] # LIMIT
for user in users:
print(user.last_name)
Менеджер и Запросы
На текущем этапе важно познакомиться с двумя терминами, которые используются повсеместно, когда речь идёт о построении запросов в Django ORM: Менеджер (Manager) и Запрос (QuerySet). Manager отвечает за связь модели как Python-класса с фактическим представлением данных в соответствующей таблице БД, а также позволяет программисту строить запросы (QuerySets) к этой таблице и создавать в ней новые записи. Помимо построения запросов, менеджер интерпретирует их результаты. Например, представляет данные из базы в виде экземпляров класса модели. У любой модели есть хотя бы один менеджер — тот, что доступен через атрибут .objects
класса модели. В примере выше User.objects
— менеджер модели User
.
С методом .create()
менеджера вы уже встречались на предыдущем уроке. Этот метод создаёт новые записи в таблице. Большинство же других методов начинают построение запроса — значения типа QuerySet
. Каждый подобный метод возвращает новый QuerySet, запоминающий новые параметры будущего запроса. В примере цепочка вызовов .filter(..).order_by(..)
конструирует запрос по шагам. И даже взятие среза ([:10]
) только уточняет запрос, также создавая новый QuerySet
.
Обратите внимание: сам SQL-запрос сразу не выполняется. Для того, чтобы выполнить SQL-запрос или, как говорят, "финализировать", нужно начать итерацию результатов или обратиться к одному из "элементов" имеющегося QuerySet по индексу. В примере SQL-запрос выполняется в тот момент, когда цикл for
пробует получить от users
первый элемент.
Построение запросов с помощью методов
Пока запрос не финализирован, его можно свободно сохранять в переменные или передавать из одного участка кода в другой и строить новые QuerySets на основе сохранённого:
johns = User.objects.filter(first_name='John') # все Джоны
connors = johns.filter(last_name='Connor') # только Джоны Конноры
others = johns.exclude(last_name='Connor') # Джоны, но не Конноры
Здесь представлено три QuerySets, причём второй и третий построены на основе первого. И ни один из них ещё не финализирован.
Обратите внимание: первый вызов в цепочке всегда относится к менеджеру, каким бы ни был метод