Миграции Django: Учебник

ОГЛАВЛЕНИЕ

  • Проблемы, которые решают миграции
    • Внесение изменений в базу данных без SQL
    • Избегайте повторения
    • Обеспечение синхронизации определений модели и схемы базы данных
    • Отслеживание изменения схемы базы данных в системе управления версиями
  • Настройка проекта Django
  • Создание миграций
  • Применение миграции
  • Смена моделей
  • Вывод списка миграций
  • Неприменимые миграции
  • Именование миграции
  • Заключение
  • видео

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

Вы узнаете, как работают Django Migrations и как получить от них максимальную отдачу, из четырех статей и одного видео:

Часть 1: Миграции Django: Учебник (текущая статья)

Часть 2: Углубляемся в миграцию

Часть 3: Миграция данных

Видео: Миграции Django 1.7 – учебник

В этой статье вы познакомитесь с миграциями Django и узнаете следующее:

  • Как создавать таблицы базы данных без написания SQL
  • Как автоматически изменять вашу базу данных после того, как вы изменили свои модели
  • Как отменить изменения, внесенные в вашу базу данных

 

ПРОБЛЕМЫ, КОТОРЫЕ РЕШАЮТ МИГРАЦИИ

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

 

Во-первых, давайте быстро определим пару терминов, чтобы убедиться, что все находятся на одной странице. Django разработан для работы с реляционной базой данных , хранящейся в системе управления реляционными базами данных, например PostgreSQL , MySQL или SQLite .

 

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

 

Все системы баз данных, поддерживаемые Django, используют язык SQL для создания, чтения, обновления и удаления данных в реляционной базе данных. SQL также используется для создания, изменения и удаления самих таблиц базы данных.

 

Работа напрямую с SQL может быть довольно обременительной, поэтому, чтобы облегчить вашу жизнь, Django поставляется с объектно-реляционным картографом, или сокращенно ORM. ORM отображает реляционную базу данных в мир объектно-ориентированного программирования. Вместо определения таблиц базы данных в SQL вы пишете модели Django на Python. Ваши модели определяют поля базы данных, которые соответствуют столбцам в их таблицах базы данных.

 

Вот пример того, как класс модели Django сопоставляется с таблицей базы данных:

Но простое определение класса модели в файле Python не приводит к волшебному появлению таблицы базы данных из ниоткуда. Создание таблиц базы данных для хранения ваших моделей Django - это задача миграции базы данных. Кроме того, всякий раз, когда вы вносите изменения в свои модели, например, добавляете поле, база данных тоже должна быть изменена. С этим справляется и миграция.

 

ВОТ НЕСКОЛЬКО СПОСОБОВ, КОТОРЫМИ МИГРАЦИИ DJANGO ОБЛЕГЧАЮТ ВАШУ ЖИЗНЬ.

 

Внесение изменений в базу данных без SQL

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

 

В Django миграции в основном написаны на Python, поэтому вам не нужно знать какой-либо SQL, если у вас нет действительно сложных вариантов использования.

Избегайте повторения

Создание модели и последующее написание SQL для создания таблиц базы данных для нее было бы повторяющимся.

 

Миграции генерируются из ваших моделей, чтобы вы не повторялись.

 

Обеспечение синхронизации определений модели и схемы базы данных

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

 

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

 

С Django Migrations вы можете легко синхронизировать несколько баз данных с вашими моделями.

 

Отслеживание изменения схемы базы данных в системе управления версиями

Система контроля версий, такая как Git , отлично подходит для кода, но не настолько для схем баз данных.

 

Поскольку миграции представляют собой простой Python в Django, вы можете поместить их в систему контроля версий, как и любой другой фрагмент кода.

 

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

 

НАСТРОЙКА ПРОЕКТА DJANGO

В этом руководстве вы будете работать над простым приложением для отслеживания биткойнов в качестве примера проекта.

 

Первый шаг - установить Django. Вот как это сделать в Linux или macOS X с использованием виртуальной среды:

$ python3 -m venvenv
$ sourceenv/bin/activate
(env)$ pip install "Django==2.1.*"
...
Successfully installed Django-2.1.3

Теперь вы создали новую виртуальную среду и активировали ее, а также установили Django в этой виртуальной среде.

 

Обратите внимание, что в Windows вы должны запустить, env/bin/activate.batа не source env/bin/activateактивировать виртуальную среду.

 

Для удобства чтения примеры консоли теперь не будут включать (env)часть подсказки.

 

Установив Django, вы можете создать проект, используя следующие команды:

$ django-admin.py startprojectbitcoin_tracker
$ cdbitcoin_tracker
$ python manage.py startapphistorical_data

Это дает вам простой проект и приложение с именем historical_data. Теперь у вас должна быть такая структура каталогов:

bitcoin_tracker/
|
├── bitcoin_tracker/
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
|
├── historical_data/
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── migrations/
│   │   └── __init__.py
|   |
│   ├── models.py
│   ├── tests.py
│   └── views.py
|
└── manage.py

Внутри bitcoin_tracker каталога есть два подкаталога: bitcoin_tracker для файлов всего проекта и historical_data содержащие файлы для созданного вами приложения.

Теперь, чтобы создать модель, добавьте этот класс в historical_data/models.py:

classPriceHistory(models.Model):
date=models.DateTimeField(auto_now_add=True)
price=models.DecimalField(max_digits=7,decimal_places=2)
volume=models.PositiveIntegerField()

Это основная модель для отслеживания цен на биткойны.

Также не забудьте добавить вновь созданное приложение в settings.INSTALLED_APPS. Откройте bitcoin_tracker/settings.pyи добавьте historical_dataв список INSTALLED_APPS, например:

INSTALLED_APPS=[
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'historical_data',
]

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

СОЗДАНИЕ МИГРАЦИЙ

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

$ python manage.py makemigrationshistorical_data
Migrations for 'historical_data':
  historical_data/migrations/0001_initial.py
- CreatemodelPriceHistory

Примечание: указывать имя приложения historical_data,, необязательно. Если оставить его выключенным, все приложения будут перенесены.

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

bitcoin_tracker/
|
├── bitcoin_tracker/
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
|
├── historical_data/
│   ├── migrations/
│   │   ├── 0001_initial.py
│   │   └── __init__.py
|   |
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
|
├── db.sqlite3
└── manage.py

Как вы можете видеть, migrations каталог теперь содержит новый файл: 0001_initial.py.

Примечание. Вы могли заметить, что при выполнении makemigrations команды также был создан файл db.sqlite3, содержащий вашу базу данных SQLite.

 

Когда вы пытаетесь получить доступ к несуществующему файлу базы данных SQLite3, он будет автоматически создан.

 

Такое поведение уникально для SQLite3. Если вы используете любую другую базу данных, например PostgreSQL или MySQL, вам необходимо создать базу данных самостоятельно перед запуском makemigrations.

Вы можете заглянуть в базу данных с помощью dbshellкоманды управления . В SQLite команда для вывода списка всех таблиц проста. tables:

$ python manage.py dbshell
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> .tables
sqlite>

База данных все еще пуста. Это изменится, когда вы примените миграцию. Введите quit для выхода из оболочки SQLite.

ПРИМЕНЕНИЕ МИГРАЦИИ

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

$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, historical_data, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying historical_data.0001_initial... OK
Applying sessions.0001_initial... OK

Здесь много всего происходит! Согласно выходным данным, ваша миграция была успешно применена. Но откуда берутся все остальные миграции?

 

Помните настройку INSTALLED_APPS? Некоторые из других приложений, перечисленных там, также идут с миграциями, и команда migrateуправления применяет миграции для всех установленных приложений по умолчанию.

 

Взгляните еще раз на базу данных:

$ python manage.py dbshell
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> .tables
auth_groupdjango_admin_log
auth_group_permissionsdjango_content_type
auth_permissiondjango_migrations
auth_userdjango_session
auth_user_groupshistorical_data_pricehistory
auth_user_user_permissions
sqlite>

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

sqlite> .schema --indent historical_data_pricehistory
CREATE TABLE IF NOT EXISTS "historical_data_pricehistory"(
  "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
  "date" datetime NOT NULL,
  "price" decimal NOT NULL,
  "volume" integer unsigned NOT NULL
);

Команда schema распечатывает CREATE инструкцию, которую вы должны выполнить для создания таблицы. Параметр indent красиво его форматирует. Даже если вы не знакомы с синтаксисом SQL, вы можете видеть, что схема historical_data_pricehistoryтаблицы отражает поля PriceHistory модели.

 

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

 

Вот что произойдет, если вы migrateснова запустите команду:

$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, historical_data, sessions
Running migrations:
  No migrations to apply.

Ничего такого! Django запоминает, какие миграции уже были применены, и не пытается их повторно запустить.

Стоит отметить, что вы также можете ограничить команду migrateуправления одним приложением:

$ python manage.py migrate historical_data
Operations to perform:
 Apply all migrations: historical_data
Running migrations:
 No migrations to apply.

Как видите, Django теперь применяет миграции только для historical_data приложения.

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

 

СМЕНА МОДЕЛЕЙ

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

 

Когда вы меняете определение модели, таблицы базы данных, используемые для хранения этих моделей, также должны быть изменены. Если определения вашей модели не соответствуют вашей текущей схеме базы данных, вы, скорее всего, столкнетесь с файлом django.db.utils.OperationalError.

 

Так как же изменить таблицы базы данных? Создав и применив миграцию.

 

Тестируя свой биткойн-трекер, вы понимаете, что совершили ошибку. Люди продают доли биткойна, поэтому поле volumeдолжно быть типа DecimalFieldвместо PositiveIntegerField.

 

Давайте изменим модель, чтобы она выглядела так:

classPriceHistory(models.Model):
date=models.DateTimeField(auto_now_add=True)
price=models.DecimalField(max_digits=7,decimal_places=2)
volume=models.DecimalField(max_digits=7,decimal_places=3)

Без миграций вам пришлось бы выяснить синтаксис SQL, чтобы PositiveIntegerFieldпреобразовать файл в DecimalField. К счастью, Django сделает это за вас. Просто скажите ему сделать миграцию:

$ python manage.py makemigrations
Migrations for 'historical_data':
  historical_data/migrations/0002_auto_20181112_1950.py
    - Alter field volume on pricehistory

Примечание. Имя файла миграции ( 0002_auto_20181112_1950.py) основано на текущем времени и будет другим, если вы будете следовать его указаниям в своей системе.

Теперь вы примените эту миграцию к своей базе данных:

$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, historical_data, sessions
Running migrations:
  Applying historical_data.0002_auto_20181112_1950... OK

Миграция была применена успешно, поэтому вы можете использовать ее dbshell для проверки того, что изменения повлияли:

$ python manage.py dbshell
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> .schema --indent historical_data_pricehistory
CREATE TABLE IF NOT EXISTS "historical_data_pricehistory" (
  "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
  "date" datetime NOT NULL,
  "price" decimal NOT NULL,
"volume" decimal NOT NULL
);

Если вы сравните новую схему со схемой, которую вы видели ранее, вы заметите, что тип volumeстолбца изменился с integerна, decimalчтобы отразить изменение volumeполя в модели с PositiveIntegerFieldна DecimalField.

ВЫВОД СПИСКА МИГРАЦИЙ

Если вы хотите знать, какие миграции существуют в проекте Django, вам не нужно копаться в migrations каталогах установленных приложений. Вы можете использовать showmigrations команду:

$ ./manage.py showmigrations
admin
 [X] 0001_initial
 [X] 0002_logentry_remove_auto_add
 [X] 0003_logentry_add_action_flag_choices
auth
 [X] 0001_initial
 [X] 0002_alter_permission_name_max_length
 [X] 0003_alter_user_email_max_length
 [X] 0004_alter_user_username_opts
 [X] 0005_alter_user_last_login_null
 [X] 0006_require_contenttypes_0002
 [X] 0007_alter_validators_add_error_messages
 [X] 0008_alter_user_username_max_length
 [X] 0009_alter_user_last_name_max_length
contenttypes
 [X] 0001_initial
 [X] 0002_remove_content_type_name
historical_data
 [X] 0001_initial
 [X] 0002_auto_20181112_1950
sessions
 [X] 0001_initial

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

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

НЕПРИМЕНИМЫЕ МИГРАЦИИ

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

 

Хотите протестировать миграцию, написал коллега

Поймите, что внесенное вами изменение было плохой идеей

Параллельная работа над несколькими функциями с разными изменениями базы данных

Хотите восстановить резервную копию, которая была создана, когда база данных все еще имела более старую схему

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

 

Если вы хотите отменить миграцию 0002_auto_20181112_1950в своем historical_data приложении, вы должны передать 0001_initialв качестве аргумента migrate команде:

Миграция не была применена, что означает, что изменения в базе данных были отменены.

 

Отмена применения миграции не приводит к удалению файла миграции. В следующий раз, когда вы запустите migrate команду, миграция будет применена снова.

Предупреждение: не путайте неприменяемые миграции с операцией отмены, к которой вы привыкли в своем любимом текстовом редакторе.

 

Не все операции с базой данных можно полностью отменить. Если вы удалите поле из модели, создадите миграцию и примените ее, Django удалит соответствующий столбец из базы данных.

 

Отмена этой миграции приведет к воссозданию столбца, но не вернет данные, которые были сохранены в этом столбце!

 

Когда вы имеете дело с именами миграции, Django избавляет вас от нескольких нажатий клавиш, не заставляя писать полное имя миграции. Ему нужно ровно столько имени, чтобы однозначно идентифицировать его.

 

В предыдущем примере достаточно было запустить python manage.py migrate historical_data 0001.

ИМЕНОВАНИЕ МИГРАЦИИ

В приведенном выше примере Django придумал имя для миграции на основе отметки времени - что-то вроде *0002_auto_20181112_1950. Если вас это не устраивает, вы можете использовать --nameпараметр, чтобы указать собственное имя (без .pyрасширения).

 

Чтобы попробовать это, сначала нужно удалить старую миграцию. Вы уже не применили его, поэтому можете безопасно удалить файл:

$ rm historical_data/migrations/0002_auto_20181112_1950.py

Теперь вы можете воссоздать его с более наглядным именем:

$ ./manage.py makemigrationshistorical_data --name switch_to_decimals

Это создаст ту же миграцию, что и раньше, за исключением нового имени 0002_switch_to_decimals.

ЗАКЛЮЧЕНИЕ

В этом руководстве вы довольно много рассмотрели и изучили основы миграции Django.

 

Напомним, что основные шаги по использованию миграции Django выглядят так:

 

Создать или обновить модель

Запустить

$./manage.py makemigrations <app_name>

Запустите, отдельное приложение

$./manage.py migrateчтобы перенести все или ./manage.py migrate <app_name>

При необходимости повторите

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

 

Если вы ранее создавали и изменяли таблицы базы данных с помощью написанного вручную SQL, теперь вы стали намного эффективнее, делегировав эту работу миграции Django.

 

В следующем руководстве этой серии вы углубитесь в тему и узнаете, как Django Migrations работает изнутри .

Оригинал статьи: