Практически любое веб-приложение так или иначе работает с базой данных. При этом с системой управления базами данных (СУБД) приложение общается универсальным способом, например, посредством языка SQL.
Программисту чаще всего хочется иметь некую абстракцию, которая позволяет большую часть времени работать с привычными сущностями языка. Такой абстракцией является ORM. В этом уроке мы разберем необходимый минимум работы с ORM. Подробнее эта тема рассматривается в отдельном курсе.
ORM
ORM (Object-Relational Mapping) — отображение сущностей предметной области и их взаимосвязей в объекты, удобные для использования программистом.
Разные ORM по-разному подходят к тому, насколько нужно изолировать пользователя от конкретного хранилища. Некоторые полностью скрывают всю работу с базой данных. В этом случае мы пользуемся объектами, изменяем их состояние, а ORM неявно синхронизирует состояние объектов и сущностей в хранилище.
Другие ORM только оборачивают сущности базы данных в структуры языка, но все запросы нужно писать вручную. Это два разных полюса, каждый со своими плюсами и минусами. Авторы Django решили остаться где-то посередине.
Если использовать Django ORM, можно работать с объектами и выполнять вручную их загрузку и сохранение. Однако для этого можно использовать привычные средства языка: итерацию, вызов методов.
Django же обеспечивает правильную работу нашего приложения с конкретными хранилищами данных. Эта изоляция от конкретного хранилища позволяет использовать разные базы данных в разных условиях. Например, при разработке и тестировании использовать что-то более легковесное, а на релизном сервере применять рабочую базу данных.
Модель
Любая сущность, которая создается внутри приложения, называется моделью. Модели в Django лежат в директориях приложений в файлах models.py. Конкретный набор моделей зависит от приложения и может измениться со временем. На Хекслете таких моделей сотни, вот лишь некоторые, с которыми наши пользователи сталкиваются каждый день:
- Пользователь
- Курс
- Урок
- Профессия
- Упражнение
- Подписка
- Участник курса
- Статья в блоге
- Топик
- Комментарий к топику
- Проект
Важно не путать понятие модель во фреймворке, с понятием модель в MVC. Последнее — это не про класс, файл, функцию или структуру данных. Это слой приложения, который отвечает за модель предметной области. И это не про базу данных.
Слово «модель» часто используется в качестве замены словосочетания «Django ORM», поэтому в рамках курсов будет использоваться этот термин. Модель в единственном числе говорит нам о том, что наша предметная область смоделирована с помощью средств фреймворка. Это собирательный образ слоя хранения. При этом отдельные сущности тоже называются моделями — модели поменьше собираются в большую модель всей предметной области.
Связь между моделями и таблицами в базе данных: одна таблица — одна модель. В этом плане Django ORM не отходит далеко от схемы базы данных. Всегда можно представить, как фактически представлены данные с точки зрения СУБД. Что очень полезно, когда нужно что-то оптимизировать.
Database Engines
К фактической базе данных Django подключается с помощью Database Engines. Одно приложение может подключаться к нескольким базам данных. Это можно сделать и с помощью разных движков. Но такое случается нечасто. Обычно одна база приходится на одно веб-приложение.
Описываются базы данных в словаре settings.DATABASES
:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
ENGINE
указывает на конкретный движокNAME
в случае SQLite хранит имя файла. Для других СУБД это будет имя базы данных в удобном для них форматеdefault
— имя базы данных уже для самого Django
Когда используется несколько баз данных одновременно и имя другой не указано в коде явно, эта «база данных по умолчанию» используется всегда.
Описание моделей
Модели описываются в модулях models.py. Каждое приложение имеет свой собственный модуль models.py, в котором содержатся описание моделей конкретного приложения.
Добавим в hexlet_django_blog.article.models
следующий код:
from django.db import models
class Article(models.Model):
name = models.CharField(max_length=200) # название статьи
body = models.TextField() # тело статьи
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
Article
— это и есть наша первая модель. У нее четыре поля:
created_at
— для хранения даты и времени создания записиupdated_at
— для хранения даты и времени последнего изменения записиname
— для хранения названия статьиbody
— для хранения текста статьи
В этом случае мы не описываем поле id
, которое обычно является первичным ключом. В models.Model оно уже описано, и нам не нужно это делать дополнительно. Но мы его можем переопределить.
Со стороны Python всё готово. Но еще нужно сделать так, чтобы соответствующая таблица появилась в базе данных. Для этого необходимо сгенерировать миграцию.
Миграция
Миграции меняют схему базы данных сообразно тому, как мы меняем модель. В Django работа с миграциями сделана на очень высоком уровне. В том числе — на высоком уровне автоматизации.
Чтобы получить миграцию, выполняем команду:
python manage.py makemigrations
Migrations for 'article':
hexlet_django_blog/article/migrations/0001_initial.py
- Create model Article
Когда мы запускаем makemigrations
, мы сообщаем Django, что внесли изменения в свои модели, например, создали новую модель. Также мы сообщаем о том, что хотим, чтобы эти изменения сохранились как миграция.
Миграции — это описания изменений в наших моделях, и в дальнейшем в схеме базы данных. Мы можем просмотреть эти файлы миграций. Они располагаются в директориях migrations внутри приложений. Например, в нашем случае файл миграции располагается в hexlet_django_blog/article/migrations/0001_initial.py.
Нам не нужно читать все файлы миграций, когда Django их создает. Но при необходимости мы можем их редактировать, чтобы изменить порядок миграции.
Теперь посмотрим, какой SQL-запрос будет выполняться при запуске миграции. Для этого воспользуемся командой sqlmigrate
:
python manage.py sqlmigrate article 0001
BEGIN;
--
-- Create model Article
--
CREATE TABLE "article_article" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(200) NOT NULL, "body" text NOT NULL, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL);
COMMIT;
- Имена таблиц автоматически генерируются с помощью объединения имени приложения (article) и имени модели в нижнем регистре — article
- Поле
id
, которое является первичным ключом, добавляется автоматически
Применяем все остальные миграции. Эта операция идемпотентна:
python manage.py migrate
Operations to perform:
...
Running migrations:
...
Applying article.0001_initial... OK
...
Созданная миграция была применена. Если все прошло успешно, то в базе данных появилась таблица article_article.
В первый раз Django применит приличное количество миграций, которые создают несколько вспомогательных таблиц. Они нужны фреймворку для подсистемы работы с пользователями и прочих вещей, которые входят в обязательный минимум.
Миграции можно не только накатывать, но и откатывать. Для этого нужно указать версию миграции, к которой мы хотим вернуться.
Например, в директории migrations приложения article у нас есть два файла миграции 0005_second_last_migration и 0006_last_migration. 0006 — это последняя примененная миграция.
Если нам нужно вернуться к миграции 0005 из миграции 0006, мы выполним следующую команду:
python manage.py migrate article 0005
Если нам нужно отменить все миграции этого приложения, нужно указать zero
в качестве версии миграции:
python manage.py migrate article zero
Иногда миграция может быть необратимой. Как правило, это возникает, когда в модели вносятся существенные изменения. Когда мы попытаемся вернуться к такой миграции, Django выдаст ошибку IrreversibleError
.
Самостоятельная работа
- Создайте модель Article
- Выполните миграции
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Вебинар «Как самостоятельно учиться»
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.