Определение моделей. Миграции: создание и выполнение

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

Архив проекта: lesson-4-coolsite.zip

На этом занятии мы с вами коснемся следующего аспекта паттерна MTV, по которому построен фреймворк Django, – модели. Как я уже ранее говорил, модель отвечает за хранение и оперирование данными сайта. Часто для этого используются стандартные СУБД, например,

SQLite, MySQL, PostgreSQL, Oracle и другие.

Причем, разработчик сайта наперед может не знать с какой именно СУБД будет работать WSGI-приложение. Да и потом, уже в процессе работы, выбор может пасть на другой тип БД. Как в этом случае построить универсальную программу не привязанную к конкретной СУБД? Для этого в Django встроен механизм взаимодействия с таблицами БД через объекты классов языка Python посредством технологии ORM (Object-Relational Mapping). Причем этот интерфейс универсален и на уровне WSGI-приложения не привязан к конкретной СУБД. (Если вы работали с SQLAlchemy, то уже хорошо понимаете о чем идет речь. Объектная модель ORM в Django очень похожа по функциональности на SQLAlchemy).

При работе с Django разработчику не нужно беспокоиться о подключении к БД и ее закрытию, когда пользователь покидает сайт. Фреймворк такие действия берет на себя и делает это весьма эффективно. Все что нам остается, это через модель взаимодействия выполнять команды API интерфейса, записывать, считывать и обновлять данные.

По умолчанию Django сконфигурирован для работы с БД SQLite и в рамках учебного проекта мы пока оставим именно ее, чтобы не перегружать материал. Текущую настройку БД можно посмотреть в файле settings.py пакета конфигурации:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

Здесь словарь DATABASES по умолчанию определяет СУБД SQLite3 и путь к файлу БД db.sqlite3.

Подключение к другим СУБД происходит относительно просто в этом же словаре DATABASES, главное, иметь драйвер взаимодействия с ними. Для SQLite ничего дополнительно устанавливать не нужно. Но для демонстрации таблиц этой БД установлю приложение DBSQLiteStudio, которое можно свободно скачать с официального сайта:

https://sqlitestudio.pl

Если мы откроем БД нашего сайта в этой программе, то увидим, что она пока не содержит ни одной таблицы. Давайте добавим первую модель, первый класс, который будет описывать таблицу women для хранения информации об известных женщинах. Структура этой таблицы будет такой:

Здесь первое поле id – это первичный ключ, принимающий уникальные числовые значения. Фактически, это идентификатор записи. Далее, идет поле title (заголовок статьи) в виде строки из определенного числа символов. Затем, поле content, представляющее собой текст статьи. После него поле photo для ссылки на первичную фотографию поста. Два следующих поля – это время создания статьи и время ее последнего изменения. Наконец, последнее поле is_published – это флаг публикации поста (True – опубликована; False – не опубликована).

Теперь, смотрите, чтобы у нас в БД появилась таблица с такой структурой нам достаточно объявить класс с этими полями, а затем, выполнить миграцию. Сначала определим класс модели. Для этого перейдем в файл women/models.py, в котором принято описывать все модели текущего приложения. И здесь вначале уже импортирован пакет models, содержащий базовый класс Model, на базе которого и строятся модели:

class Women(models.Model):
    title = models.CharField(max_length=255)
    content = models.TextField(blank=True)
    photo = models.ImageField(upload_to="photos/%Y/%m/%d/")
    time_create = models.DateTimeField(auto_now_add=True)
    time_update = models.DateTimeField(auto_now=True)
    is_published = models.BooleanField(default=True)

По умолчанию имя таблицы будет совпадать с именем класса, а ее структура определяться атрибутами, которые мы здесь определили. Давайте внимательнее посмотрим, что здесь написано. Во-первых, нигде нет поля id. Но ошибки нет, такое поле создается автоматически у каждой таблицы. В действительности, оно уже прописано в базовом классе Model по всем правилам. Далее, атрибут title будет определять одноименное поле как текстовую строку с максимальным числом символов 255. Но откуда мне было известно, что для этого следовало использовать класс CharField с параметром max_length? Как вы уже догадались, все это приведено в документации по фреймворку Django. Например, можно обратиться к русскоязычной документации:

https://djbook.ru/rel3.0/

и в разделе «ORM и работа с базой данных» перейти по ссылке «Типы полей»:

https://djbook.ru/rel3.0/ref/models/fields.html

Здесь внушительный список самых разных классов, описывающих поля модели, и если щелкнуть по CharFiled, то увидим его описание и тот самый параметр max_length, что был использован для указания максимального числа символов в строке. И так по всем полям. Я рекомендую вам ознакомиться с этой информацией, чтобы вы в целом знали какие поля, когда использовать.

Итак, первые два поля (id и title) определены. Следующее поле content задано как текстовое с параметром blank=True. Данный параметр означает, что это поле может быть пустым, то есть, не содержать текста. Я привел его, скорее, для ознакомления. В реальной статье вряд ли следует ожидать пустое содержимое. После него идет атрибут photo, определяемый классом ImageField. Этот класс автоматизирует процесс загрузки изображений на сервер и располагает их в папке, указанной через параметр upload_to. Причем, в этом параметре мы можем прописывать шаблон. Например, «%Y/%m/%d» описывает вложенные папки как год, месяц, день. Это очень удобно для нагруженных проектов с большим числом изображений и чтобы они распределялись по подкаталогам равномерно можно использовать такие шаблоны. При этом поле photo будет содержать полный путь к файлу изображения, то есть, в БД оно будет представлено обычным текстовым полем.

Следующие два атрибута определены классом DateTimeField, предназначенный специально для работы со временем. У него есть два параметра:

  • auto_now_add – позволяет фиксировать текущее время только в момент первого добавления записи в таблицу БД;
  • auto_now – фиксирует текущее время всякий раз при изменении или добавлении записи в таблицу БД.

Эти параметры нам как раз подходят для формирования времени атрибутов time_create и time_update.

Последний атрибут is_published определен через класс BooleanField с параметром default=True. Это означает, что по умолчанию значение поля в БД будет установлено в значение True и статья будет считаться опубликованной.

Это лишь пример того, как можно описывать модель таблиц в БД. Причем, последовательность полей в таблице, по умолчанию, будет такой же как и в представленной модели.

Настройка для поля ImageField

Вернемся теперь к полю ImageField. Как я сказал, оно будет хранить лишь путь к файлу в таблице БД. И чтобы Django имел возможность автоматически выполнять операции загрузки графических файлов и формировать путь к нему, необходимо задать и настроить две константы в нашем приложении: MEDIA_ROOT и MEDIA_URL. Подробное описание о них можно посмотреть в документации о поле FileField:

https://djbook.ru/rel3.0/ref/models/fields.html#django.db.models.FileField

В пакете конфигурации открываем файл settings.py и в конце допишем эти константы, следующим образом:

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'

Соответственно, MEDIA_ROOT будет ссылаться на папку media, расположенной в текущем рабочем каталоге проекта (Django ее создаст автоматически). А MEDIA_URL будет добавлять к URL графических файлов префикс media.

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

if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Мы здесь к уже существующим маршрутам добавляем еще один маршрут для статических данных – графических файлов, указывая вначале URL, а затем, корневую папку, где они будут храниться. Причем все это делается только в отладочном режиме. На реальных серверах этот процесс, как правило, уже настроен и нам ничего дополнительно делать не нужно.

Создание и запуск миграций приложения

Как же создать таблицу в БД на основе описанной модели? Для этого в Django (да и не только в нем) существует механизм, известный как создание и выполнение миграций для БД. Что такое миграции? Фактически, это модули языка Python, где описаны наборы команд на уровне ORM интерфейса, для создания таблиц определенных структур. При выполнении файла миграции в БД автоматически создаются новые или изменяются прежние таблицы, а также связи между ними. Каждый новый файл миграции помещается в папку migrations текущего приложения и описывает лишь изменения, которые произошли в структурах таблиц с прошлого раза. Их можно воспринимать как контролеры версий: всегда можно откатиться к предыдущей структуре и продолжить работу с прежней версией структур и связей между таблицами.

Конечно, при разработке сайтов структуру таблиц и их связей лучше продумывать заранее и потом не менять, так как любое такое изменение, как правило, влияет на программный код и уж точно придется создавать очередную миграцию для обновления БД. В учебных целях я покажу как изменять структуры таблиц, но на практике этого лучше избегать.

Итак, у нас пока нет ни одной миграции. Давайте ее создадим. Для этого переходим в терминал и из корневого каталога проекта обращаемся к модулю manage.py и выполняем команду:

python manage.py makemigrations

У меня отобразилась ошибка, что не установлен модуль Pillow, необходимый для обработки поля ImageFiled. Хорошо, установим его:

pip install pillow

И снова выполним команду makemigrations. Теперь файл миграции был успешно создан с именем 0001_initial.py и помещен в каталог migrations. Если открыть этот файл, то увидим команду CreateModel, которая создает таблицу women с указанным набором полей, включая поле id.

Чтобы посмотреть SQL-запрос, который будет выполнен при использовании данной миграции, можно записать следующую команду sqlmigrate:

python manage.py sqlmigrate women 0001

После этой команды мы указываем название приложения и порядковый номер миграции (только номер без _initial.py). Выполняя ее, в консоли увидим соответствующий SQL-запрос. Конечно, этот запрос может меняться в зависимости от выбранного типа БД.

Давайте выполним все наши миграции в приложении. На самом деле их будет несколько, так как есть стандартные (уже заготовленные) файлы миграций, связанные с админкой, авторизацией, сессиями и так далее, то есть с модулями, которые поставляются в коробке вместе с Django. Итак, для выполнения миграций запишем команду:

python manage.py migrate

Все выполнилось успешно и если открыть файл БД в DBSQLiteStudio, то мы увидим в ней одиннадцать таблиц, в том числе и нашу таблицу women_women. Ее имя было определено по имени приложения Women и имени модели, которое также Women. Если дважды щелкнуть на таблицу, то увидим ее структуру именно ту, что определена в нашей модели.

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

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

Видео по теме