orm django что это
Django ORM для начинающих | Оптимизируем запросы
Django ORM (Object Relational Mapping) является одной из самых мощных особенностей Django. Это позволяет нам взаимодействовать с базой данных, используя код Python, а не SQL.
Based on schegel.net
Для демонстрации опишу такую модель:
Я буду использовать django-extentions, чтобы получить полезную информацию с помощью:
1. Используем ForeignKey значения непосредственно
А так получаем 1 запрос в БД:
2. OneToMany Relations
Если мы используем OneToMany отношения мы используем ForeignKey поля и запрос выглядит примерно так:
И если мы хотим получить доступ к объекту блога из объекта поста, мы можем сделать:
Тем не менее, это вызвало новый запрос, чтобы получить информацию из блога. Так что используйте select_related, чтобы избежать этого. Чтобы использовать его, мы можем обновить наш оригинальный запрос:
Обратите внимание, что Django использует JOIN сейчас! И время выполнения запроса меньше, чем раньше. Кроме того, теперь post.blog будет кэширован!
select_related так же работает с QurySets:
3. ManyToMany Relations
Чтобы получить авторов постов мы используем что-то вроде этого:
Похоже, мы получили запрос для каждого объекта поста. По этому, мы должны использовать prefetch_related. Это похоже на select_related но используется с ManyToMany Fields:
Что только что произошло. Мы сократили количество запросов с 2 до 1, чтобы получить 2 QuerySet-a!
4. Prefetch object
prefetch_related достаточно для большинства случаев, но это не всегда помогает избежать дополнительных запросовю К примеру, если мы используем фильтрацию Django не может использовать наши кэшированные posts, так как они не были отфильтрованы, когда они были запрошены в первом запросе. И мы получим:
То есть, мы использовали prefetch_related, чтобы уменьшить количество запросов, но мы фактически увеличили его. Чтобы этого избежать, мы можем настроить запрос с помощью объекта Prefetch:
Мы использовали определенный запрос для получения постов через параметр запроса и сохранили отфильтрованные сообщения в новом атрибуте. Как мы видим, теперь у нас есть только 2 запроса в базу данных.
Prefetch
Метод prefetch_related позволяет нам уменьшить количество запросов к БД, если вы хотите выгрузить связанные с моделью данные в отношении один ко многим или многие ко многим. Допустим, есть у нас две модели:
Если мы хотим выгрузить всех авторов вместе со всеми книгами и сократить количество запросов до минимума, мы сделаем так:
При такой конструкции джанго будет использовать два запроса к БД:
чтобы стянуть всех авторов;
чтобы стянуть все книги, привязанные к этим авторам (а также это привяжет книги к авторам).
И тогда мы сможем обращаться к книгам автора:
С точки зрения SQL это будет выглядеть так: первый запрос:
И тут мы встретимся с первой неожиданностью. Такая конструкция на каждого автора будет делать дополнительный запрос, чтобы стянуть все привязанные к нему книги начинающиеся на “А”. А то, что вы “спрефетчили” в первой строке вообще будет проигнорировано. Это происходит потому, что данные загруженные через prefetch_related кешируются только для запросов типа all (смотри первый пример). Т.е. никаких фильтров, аннотаций и прочих прелестей использовать нельзя. Вторая идея, которая может возникнуть, выглядит так:
И снова будем неправы, ибо такой запрос не просто отфильтрует нам книги, но также уберет из ответа всех авторов, у которых нет книг начинающихся на “А”. В общем, чтобы работать с данными, загружаемыми через prefetch_related, предусмотрен специальный класс Prefetch, который позволяет нам превратить поведение prefetch_related в некое подобие annotate, но для связных данных.
Класс Prefetch
Чтобы наверняка решить проблему, возникшую выше, необходимо воспользоваться классом Prefetch, который позволяет фильтровать, добавлять аннотации и т. д. к связным наборам. Делается это так:
У конструктора Prefetch три параметра:
Стоит уточнить, что по дизайну в аргументе queryset полагается использовать ORM запрос с той же моделью что и у связанного набора, но если вам вдруг захочется подставить что-то другое, то возможно оно и сработает, но не факт, что так как ожидаемо, или вообще с ошибкой:
В общем, не надо так.
Также есть ограничение на использование метода values в ORM запросе. Так, попробовав вызвать:
вы получите ошибку:
Зато only можно использовать во всей красе. Ну и самый замечательный параметр, так это to_attr. Используя его, вы можете добавлять сколько угодно атрибутов, каждый из которых будет содержать независимый набор данных, например:
Еще раз прошу обратить внимание, что при доступе к prefetch-наборам, мы всегда обязательно используем метод all().
Метод raw
Это штуковина остается на крайний случай когда все остальные способы уже перепробованны. Главным условием является, чтобы в запросе, в результате присутствовало поле id.
Допустим у вас хитрый join с несколькими условиями, и какое-то поле, которое формируется на основании этого джоина. Что-то в этом роде:
и после этого я могу обращатся к полю limit_real, как будето оно является частью модели Table (то есть примерно как ведет себя функция annotate):
Также не стоит забывать, что Django в этом случае не подтягивает поля не указанные в raw-запросе. И попытка обратится к полю, которое не было указано в запросе, повлечет за собой дополнительное обращение к БД.
Главным недостатком этого метода, на мой взгляд, является не возможность использовать chain-методы. То есть навесить лишний filter, annotate, order_by, не получится. Хотите добавить условную фильтрацию извольте работать со текстом запроса вручную.
Функция RawSQL
Более мягким вариантом, когда без сложного запроса не обойтись, но хочется сохранить функционал filter, annotate, order_by, является использование RawSQL. В этом случае вам необходимо сформировать запрос который возвращает только один столбец (в большинстве случаев это id), например:
А далее передать этот запрос в качестве условия фильтрации по id, но обернув его в RawSQL:
Такой подход повзоляет реализовать сложную фильтрация, и в тоже время практически полностью остаться в рамках Django ORM
Заключение
django, orm, python, sql
Что такое Django ORM?
Базы данных являются одной из самых популярных технологий, используемых для сбора и организации данных, поскольку они позволяют легко получить доступ к данным, управлять ими и обновлять их. Однако этим базам данных требуется система управления для выполнения этих задач. В основном язык SQL используется для выполнения операций в базе данных, однако по мере того, как ваше приложение растет и становится более сложным, становится чрезвычайно трудно иметь представление о том, что именно делает каждая операция. Именно здесь на сцену выходит техника объектно-реляционного сопоставления (ORM). Это позволяет запрашивать данные и манипулировать ими с помощью любого объектно-ориентированного языка программирования по вашему выбору. ORM уменьшают сложность вашего кода и делают его более понятным, что, в свою очередь, упрощает обновление, поддержку и повторное использование кода.
В этой статье мы рассмотрим Django ORM, который представляет собой ORM на основе Python и, следовательно, является одной из самых популярных технологий, используемых в наши дни.
Что такое Django?
Прежде чем мы перейдем к рассмотрению ORM Django, давайте сначала посмотрим, что на самом деле представляет собой эта технология Pythonic под названием Django.
Django – это бесплатный веб-фреймворк с открытым исходным кодом, разработанный на Python, поэтому он имеет очень чистый и аккуратный дизайн, а также простой, гибкий, надежный и масштабируемый. Он упрощает работу веб-разработчиков, поскольку предоставляет пользователям готовые компоненты, которые, в свою очередь, не позволяют им писать все с нуля и, как следствие, ускоряют их работу и сокращают накладные расходы на их веб-сайтах. В дополнение к этому, он чрезвычайно безопасен и помогает пользователям избежать проблем с безопасностью, таких как атаки восстановления пользовательского интерфейса, SQL-инъекции и т. д. У него также очень большое сообщество, которое всегда доступно через форумы и всегда готово предложить свою помощь другим.
Давайте теперь наконец посмотрим на Django ORM и некоторые из его основных функций.
Доступ к Django ORM
После установки Django и настройки его проекта нам обычно предоставляются следующие исходные файлы:
Здесь mysite относится к имени созданного вами проекта. Все эти файлы используются по-своему, и важно знать, какую роль играет каждый файл. Здесь мы сосредоточимся на файле manage.py, который будет контролировать для нас множество различных вещей, таких как настройка сервера, выполнение миграций, связь с базой данных, а также вход в режим ORM.
Чтобы открыть Django ORM, откройте командную строку из основного каталога вашего проекта Django и выполните следующую команду:
Это откроет для нас интерактивную оболочку, которая позволит нам начать взаимодействие с базой данных с помощью ORM.
Управление базой данных с помощью запросов в Django ORM
Поскольку ORM позволяет нам взаимодействовать с базой данных, теперь мы можем писать различные запросы для извлечения и управления данными из базы данных. Однако, прежде чем мы сможем начать работу с базой данных в оболочке, мы сначала должны импортировать все связанные с ней модели. Это можно сделать, просто запустив команду в интерактивной оболочке, как показано ниже:
Здесь appName относится к имени вашего приложения, которое вы создали, и, следовательно, где в настоящее время хранятся ваши модели. ModelName относится к имени модели, которую вы хотите импортировать и использовать. Здесь вы можете импортировать несколько моделей.
Теперь вы можете получить доступ к объекту модели и прочитать из него данные. Например, если нам нужен список всех сообщений, мы можем просто получить их, выполнив следующую команду в нашем терминале:
В ORM мы можем делать несколько других вещей, таких как создание новых данных базы данных, обновление данных и все другие команды базы данных, которые вы можете.
Моделирование базы данных
Одна из лучших вещей, которые Django ORM предоставляет своим пользователям, – это возможность автоматически связывать и устанавливать отношения между атрибутами объекта вашей модели и соответствующими полями таблицы. В базах данных существуют в основном три типа отношений. это отношения «один к одному», отношения «один ко многим» или «многие к одному» и отношения «многие ко многим».
Отношение «один к одному», как следует из названия, означает, что запись одной таблицы соответствует одной записи другой таблицы. В Django ORM мы можем легко установить это следующим образом:
Здесь у каждого пользователя могут быть только одинокие биологические родители, и, следовательно, это отношения один-к-одному. Теперь, если мы удалим любого пользователя, имеющего доступ к этой модели, он также удалит модель 2-го пользователя, поскольку они зависят друг от друга.
Отношения «один ко многим» или «многие к одному» относятся к отношениям, при которых родительская запись может иметь несколько дочерних записей, однако у нее может быть только один дочерний элемент или ни одного дочернего. В Django ORM мы можем легко установить эту связь с помощью поля ForeignKey:
Как видно из приведенного выше кода, у клиента может быть несколько транспортных средств.
Наконец, отношения «многие ко многим» определяют отношения, при которых несколько таблиц могут быть связаны друг с другом. Мы можем создать это с помощью поля ManyToMany. В приведенном ниже примере мы создали две модели: одну для пользователя, а другую – для его сообщений. Также может быть несколько пользователей, поскольку у каждого пользователя может быть несколько сообщений.
Вывод
Django ORM – чрезвычайно мощный инструмент, который значительно упростил работу веб-разработчиков. Он имеет множество функций, таких как манипулирование моделями базы данных, установление отношений между моделями и многое другое. Вкратце, Django ORM – одна из лучших вещей, поставляемых с Django, и очень эффективная при выполнении поставленной перед ним задачи.
Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.
Фичи Django ORM, о которых вы не знали
Авторизуйтесь
Фичи Django ORM, о которых вы не знали
ORM весьма полезны для разработчиков, но абстрагирование доступа к базе данных имеет свою цену. Разработчики, которые решили покопаться в базе данных, обнаруживают, что некоторые вещи можно было сделать проще. Представляем вашему вниманию 9 советов по работе с базами данных в Django.
Агрегация с filter
До Django 2.0, если мы хотели получить что-нибудь вроде общего количества пользователей и общего количества активных пользователей, приходилось пользоваться условными выражениями:
В Django 2.0 был добавлен аргумент filter для агрегатных функций, что сильно упростило процесс:
Если вы используете PostgreSQL, то два запроса будут выглядеть следующим образом:
Результаты QuerySet в виде именованного кортежа
Пользовательские функции
ORM Django довольно многофункциональный, но тем не менее не может поспевать за всеми вендорами баз данных. К счастью, существует возможность дополнять его пользовательскими функциями.
Здорово, но одно только среднее значение нам мало что даёт. Давайте попробуем получить ещё и среднеквадратичное отклонение:
20–22 декабря, Онлайн, Беcплатно
Как вариант можно использовать функцию EXTRACT :
А как это реализовать в Django? Правильно, с помощью пользовательских функций:
В итоге наша функция выглядит следующим образом:
Ограничиваем время выполнения запроса
Это, наверное, самый простой и самый важный совет. Все мы люди и нам свойственно совершать ошибки. Мы не можем абсолютно всё предусмотреть наперёд.
В отличие от других неблокирующих серверов приложений вроде Tornado, asyncio и даже Node, Django обычно использует синхронные рабочие процессы. Это значит, что, когда пользователь выполняет длительную операцию, рабочий процесс останавливается, и никто не может его использовать до тех пор, пока операция не завершится.
Конечно, вряд ли кто-то использует Django в продакшене только с одним процессом.
В большинстве Django-приложений большая часть времени тратится на ожидание запросов к базе данных. Поэтому будет неплохо начать с ограничения времени выполнения SQL-запросов.
Можно установить глобальный тайм-аут таким образом:
Тайм-аут также можно установить на уровне пользователя:
Примечание На сетевые взаимодействия можно потратить уйму времени. Поэтому при вызове удалённой службы убедитесь, что установили тайм-аут:
LIMIT
Это немного связано с предыдущим пунктом в плане ограничений. Иногда мы хотим, чтобы пользователи могли создавать отчёты и, возможно, экспортировать их в таблицу. Такая функциональность всегда находится под подозорением, когда что-то идёт не так.
Нередко встречаются пользователи, которые считают целесообразным экспортировать все записи с начала времён в разгар рабочего дня. Также такие пользователи зачастую считают своим долгом открыть другую вкладку и попробовать снова, т.к. прошлая попытка успехом не увенчалась.
И здесь на помощь спешит LIMIT.
Давайте попробуем ограничить какой-нибудь запрос сотней записей:
Это — худшее, что вы можете сделать. Вы просто поместили дофигаллион записей в память, чтобы вернуть всего лишь первые 100 из них.
Уже лучше. Django использует оператор SQL limit для получения только первых ста записей.
Итак, ограничение добавили, пользователи под контролем, всё хорошо. Но одна проблема все ещё есть — пользователь запросил все записи, и мы вернули ему 100. Теперь он думает, что их всего 100, что, конечно же, не так.
Вместо того чтобы просто возвращать первые 100 записей, давайте будем выбрасывать исключение, если записей на самом деле больше:
Это сработает, но мы просто добавили ещё один запрос.
Можем ли мы сделать лучше? Наверное, можем:
Вместо того, чтобы запросить первые 100 записей, мы запрашиваем 100 + 1 = 101 запись. Если 101 запись существует, то этого достаточно, чтобы понять, что записей больше 100. Другими словами, запрос LIMIT + 1 записи — меньшее, что можно сделать, чтобы убедиться, что запрос вернёт количество записей не больше, чем LIMIT.
Запомните этот трюк с LIMIT + 1, порой может пригодиться.
Select for update … of
К этому мы пришли через боль и страдания. Посреди ночи у нас начали появляться ошибки по поводу тайм-аутов транзакций из-за блокировок в базе данных.
Общая схема работы с транзакциями в нашем коде выглядит следующим образом:
Управление транзакциями обычно связано со свойствами пользователя и продукта, поэтому мы часто использовали select_related для принудительного объединения и сохранения некоторых запросов.
Обновление транзакции также предполагает блокировку, чтобы быть уверенным, что больше никто с ней не взаимодействует.
Итак, вы видите, в чём проблема? Нет? Мы тоже не видели.
У нас были ETL-процессы, работающие в ночное время с таблицами пользователей и продуктов. Эти ETL выполняли обновление и вставку, поэтому они также блокировали таблицы.
Код, который мы использовали, пытался заблокировать как таблицу транзакций, так и таблицы пользователей, продуктов и категорий. Когда ETC блокировал последние три таблицы в середине ночи, транзакции начинали давать сбой.
Как только мы поняли, в чём суть проблемы, мы стали искать способ заблокировать только нужную таблицу — таблицу транзакций. К счастью, новая опция для select_for_update как раз стала доступна в Django 2.0:
На данный момент эта опция доступна только для PostgreSQL и Oracle.
Индексы внешних ключей
При создании модели Django автоматически создаёт B-Tree индекс для любого внешнего ключа. Эти индексы порой занимают много места и в то же время не всегда нужны.
Классический пример — модель с отношением многие-ко-многим:
Другим типичным поведением в моделях многие-ко-многим является добавление уникальных ограничений на два поля. В нашем случае это означает, что пользователь может быть членом одной группы только один раз:
Кроме того, unique_together создаст индекс для обоих полей. Таким образом, мы получаем одну модель с двумя полями и тремя индексами.
В зависимости от того, что мы собираемся делать с моделью, во многих случаях мы можем отбросить индексы внешних ключей и оставить только созданный уникальным ограничением:
Удаление лишних индексов сделает вставку и обновление быстрее, кроме того, наша база данных теперь весит меньше, что всегда хорошо.
Порядок столбцов в составном индексе
Индексы с более чем одним столбцом называются составными. В составных индексах В-Tree первый столбец индексируется с помощью древовидной структуры. Из листьев первого уровня создаются деревья для второго уровня и так далее.
Порядок столбцов в индексе очень важен.
В примере выше у нас бы создавалось дерево сначала для групп, а затем для каждой группы ещё по дереву для всех пользователей в группе.
Секрет состоит в том, чтобы в составных индексах B-Tree делать вторичные индексы как можно меньше. Другими словами, столбцы с большей кардинальностью (больше разных значений) должны идти первыми.
В нашем примере разумно предположить, что пользователей больше, чем групп, поэтому столбец с пользователями мы выносим на первый план, чтобы сделать вторичный индекс меньше:
Это всего лишь совет, а не правило, которого нужно обязательно придерживаться. Конечная индексация должна быть оптимизирована в каждом случае соответствующим образом. На что нужно обратить внимание, так это на неявные индексы и важность порядка столбцов в составных индексах.
BRIN-индексы
Индекс B-Tree имеет структуру дерева. Стоимость поиска одного значения — это высота дерева + 1 при случайном доступе к дереву. Это делает B-Tree индексы идеальными для уникальных ограничений и некоторых диапазонных запросов.
Проблема B-Tree индексов заключается в том, что они могут занимать много места.
Зачастую кажется, что нет никаких альтернатив, но базы данных предлагают другие типы индексов для разных случаев.
В Django 1.11 появилась новая опция Meta для создания индексов на модели. Это даёт нам возможность изучить другие типы индексов.
В PostgreSQL есть очень полезный тип индексов под названием BRIN (Block Range Index). В некоторых случаях этот тип индексов может быть более эффективным, чем B-Tree.
Давайте посмотрим, что об этом говорится в официальной документации:
BRIN предназначен для обработки очень больших таблиц, в которых значение индексируемого столбца имеет некоторую естественную корреляцию с физическим положением строки в таблице.
Чтобы понять это утверждение, важно понимать, как работает индекс BRIN. Как следует из названия, BRIN создаёт мини-индекс по ряду соседних блоков в таблице. Индекс очень маленький, и он может только сказать, находится ли определённое значние или нет в диапазоне индексированных блоков.
Давайте посмотрим на упрощённый пример работы BRIN, чтобы лучше со всем разобраться.
Допустим, у нас есть эти значения в столбце, каждое из которых является одним блоком:
Теперь создадим три диапазона для смежных блоков:
Для каждого диапазона мы будем хранить его минимальное и максимальное значения:
Попробуем найти 5, используя этот индекс:
Благодаря этому индексу наш поиск ограничивается блоками 4–6.
Возьмём другой пример, в этот раз значения уже не отсортированы:
А вот наш индекс с минимальным и максимальным значениями для каждого диапазона:
Этот индекс бесполезен — он не просто не ограничил наш поиск, но ещё и вынудил нас проделать больше работы, так как мы запросили и индекс, и всю таблицу.
Вернёмся к документации:
…значение индексируемого столбца имеет некоторую естественную корреляцию с физическим положением строки в таблице.
Вот оно что. Чтобы извлечь из BRIN-индекса максимальную пользу, значения в столбце должны быть отсортированы или сгруппированы на диске.
Возвращаясь к Django, какое поле у нас часто индексируется и с наибольшей вероятностью будет естественным образом отсортировано на диске? Правильно, auto_now_add.
Обычно в моделях Django это выглядит так:
При использовании auto_now_add Django автоматически заполняет поле временем создания записи. Созданное поле зачастую используется в запросах, поэтому его часто индексируют.
Чтобы ощутить разницу в размерах, создадим таблицу с
2 млн записей, где есть отсортированное поле даты:
B-Tree индекс: 37 MB
BRIN индекс: 49 KB
Всё верно, здесь нет никакой ошибки.
При создании индексов следует учитывать не только их размер. Однако теперь, учитывая поддержку индексов с Django 1.11, мы можем легко добавлять новые типы индексов в наши приложения и делать их легче и быстрее.
Основные концепции — Python: Django ORM
Это обзорный урок, который показывает общие концепции присущие ORM. Каждая из этих концепций будет далее рассматриваться в курсе
ORM (Object-Relation Mapping) – общее название для фреймворков или библиотек, позволяющих автоматически связать базу данных с кодом. Они стараются скрыть существование базы данных настолько, насколько это возможно. Взамен программисту дают возможность оперировать данными в базе через специальный интерфейс. Вместо построения SQL-запросов, программист вызывает простые методы, а всю остальную работу берёт на себя ORM.
Несмотря на общую цель, ORM бывают очень разными. Django ORM относится к наиболее распространённому и простому типу ORM, реализующему шаблон проектирования Active Record. Этот шаблон базируется на идее, что каждой таблице в приложении соответствует один класс (модель). Этот класс отвечает как за реализацию бизнес логики, так и за взаимодействие с базой данных. Последнее обычно появляется в модели за счёт наследования от базового класса ORM.
Кроме Active Record существует шаблон Data Mapper, он например реализован в SQLAlchemy. Этот подход разделяет сущности и код, связанный с базой данных, на два независимых слоя. Такой подход гибче, но при этом сложнее в работе.
Модель
Каждый объект данного класса соответствует одной записи в таблице. За преобразования данных в объекты и обратно, отвечает ORM. Программисту лишь нужно использовать правильные методы для извлечения объектов и их модификации.
Выборки
Важная часть любой ORM — это Query Builder (построитель запросов). Это абстракция поверх SQL, которая упрощает генерацию запросов. Она обычно выглядит как цепочка функций, каждая из которых отвечает за конкретную часть SQL, например: ORDER, SELECT или WHERE.
Django ORM позволяет описывать достаточно сложные запросы и большинство задач вы сможете решать именно с помощью Query Builder. Однако существует и возможность запросы напрямую:
Мы не рекомендуем по умолчанию использовать запросы, написанные вручную. Оставьте эту возможность для тех немногих случаев, когда Query Builder не может сформировать эффективный запрос.
Схема
Ещё одна обязанность ORM – изменение схемы базы данных: добавление, удаление и модификация таблиц. Делается это, как правило, не на чистом SQL, а с помощью специального языка. Это позволяет работать с ORM, не отвлекаясь на особенности конкретных баз данных.
В Django ORM первостепенными являются модели — вы изменяете код, а потом используете средства для приведения схемы базы данных в соответствие новому состоянию кода. Такой подход называется Code First.
Также существует подход Database First: вы сначала меняете базу данных, выполняя SQL-запросы или формируя запросы с помощью средств ORM, а затем ORM уже сама подхватывает изменения и адаптирует модели. Например для добавления нового свойства достаточно добавить новую колонку. В коде ничего менять не нужно, она автоматически начинает работать.
Миграции
Любая база данных в процессе жизни приложения изменяется. В неё добавляются новые таблицы, удаляются старые, какие-то меняются. Этот процесс никогда не заканчивается. Изменения в базе данных выполняются с помощью механизма миграций.
В Django ORM миграция — это Python модуль, который содержит описание действий, изменяющих схему базы данных. Учебный проект содержит пример миграции, но если вы этот файл откроете в редакторе, количество кода может вас смутить. К счастью, этот код не написан вручную — миграции Django генерирует самостоятельно. Модифицировать же вручную модули миграций приходится достаточно редко.
Попробуем добавить поле «nickname» к полям модели User из учебного проекта:
Модель изменилась, нужно сгенерировать миграцию: