Гледайте сега Този урок има свързан видео курс, създаден от екипа на Real Python. Гледайте го заедно с писмения урок, за да задълбочите разбирането си:Django Migrations 101
От версия 1.7 Django се предлага с вградена поддръжка за миграции на бази данни. В Django миграциите на бази данни обикновено вървят ръка за ръка с модели:всеки път, когато кодирате нов модел, вие също генерирате миграция, за да създадете необходимата таблица в базата данни. Въпреки това, миграциите могат да направят много повече.
Ще научите как работят Django Migrations и как можете да извлечете максимума от тях в хода на четири статии и един видеоклип:
- Част 1:Django Migrations:A Primer (текуща статия)
- Част 2:Копаене по-дълбоко в миграциите
- Част 3:Миграции на данни
- Видео:Миграции на Django 1.7 – начален курс
В тази статия ще се запознаете с миграциите на Django и ще научите следното:
- Как да създадете таблици на база данни, без да пишете SQL
- Как автоматично да променяте вашата база данни, след като сте променили моделите си
- Как да върнете промените, направени във вашата база данни
Безплатен бонус: Щракнете тук, за да получите достъп до безплатно ръководство за учебни ресурси на Django (PDF), което ви показва съвети и трикове, както и често срещани клопки, които трябва да избягвате, когато създавате уеб приложения на Python + Django.
Проблемите, които миграцията решава
Ако сте нов в 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 venv env
$ source env/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 startproject bitcoin_tracker
$ cd bitcoin_tracker
$ python manage.py startapp historical_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
:
class PriceHistory(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 makemigrations historical_data
Migrations for 'historical_data':
historical_data/migrations/0001_initial.py
- Create model PriceHistory
Забележка: Указване на името на приложението, 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_group django_admin_log
auth_group_permissions django_content_type
auth_permission django_migrations
auth_user django_session
auth_user_groups historical_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
.
Нека променим модела, за да изглежда така:
class PriceHistory(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
команда:
$ python manage.py migrate historical_data 0001_initial
Operations to perform:
Target specific migration: 0001_initial, from historical_data
Running migrations:
Rendering model states... DONE
Unapplying historical_data.0002_auto_20181112_1950... OK
Миграцията не е приложена, което означава, че промените в базата данни са отменени.
Прекратяването на прилагането на миграция не премахва нейния файл за миграция. Следващия път, когато стартирате 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 makemigrations historical_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 Migration работят под капака.
Безплатен бонус: Щракнете тук, за да получите достъп до безплатно ръководство за учебни ресурси на Django (PDF), което ви показва съвети и трикове, както и често срещани клопки, които трябва да избягвате, когато създавате уеб приложения на Python + Django.
Наздраве!