Database
 sql >> база данни >  >> RDS >> Database

Копаене по-дълбоко в миграциите на Django

Това е втората статия от нашата серия за миграции на Django:

  • Част 1:Django Migrations:A Primer
  • Част 2:Копаене по-дълбоко в миграциите на Django (текуща статия)
  • Част 3:Миграции на данни
  • Видео:Django 1.7 Migrations – A Primer

В предишната статия от тази серия научихте за целта на миграциите на Django. Запознали сте се с основните модели на използване като създаване и прилагане на миграции. Сега е време да копаем по-дълбоко в системата за миграция и да надникнем в някои от основните й механизми.

До края на тази статия ще знаете:

  • Как Django следи миграциите
  • Как миграциите знаят кои операции с база данни да извършат
  • Как се дефинират зависимостите между миграциите

След като се запознаете с тази част от системата за миграция на Django, ще сте добре подготвени да създадете свои собствени персонализирани миграции. Нека скочим точно там, където спряхме!

Тази статия използва bitcoin_tracker Проект Django, изграден в Django Migrations:A Primer. Можете или да създадете отново този проект, като работите през тази статия, или можете да изтеглите изходния код:

Изтеглете изходния код: Щракнете тук, за да изтеглите кода за проекта за миграции на Django, който ще използвате в тази статия.


Как Django знае кои миграции да приложи

Нека обобщим последната стъпка от предишната статия от поредицата. Създадохте миграция и след това приложихте всички налични миграции с python manage.py migrate .Ако тази команда се изпълни успешно, тогава таблиците на вашата база данни вече съответстват на дефинициите на вашия модел.

Какво ще стане, ако изпълните тази команда отново? Нека го изпробваме:

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

Нищо не се е случило! След като се приложи миграция към база данни, Django няма да приложи отново тази миграция към тази конкретна база данни. За да се гарантира, че миграцията се прилага само веднъж, изисква проследяване на приложените миграции.

Django използва таблица на база данни, наречена django_migrations . Django автоматично създава тази таблица във вашата база данни при първото прилагане на миграции. За всяка миграция, която е приложена или фалшифицирана, в таблицата се вмъква нов ред.

Например, ето как изглежда тази таблица в нашия bitcoin_tracker проект:

ID Приложение Име Приложено
1 contenttypes 0001_initial 2019-02-05 20:23:21.461496
2 auth 0001_initial 2019-02-05 20:23:21.489948
3 admin 0001_initial 2019-02-05 20:23:21.508742
4 admin 0002_logentry_remove... 2019-02-05 20:23:21.531390
5 admin 0003_logentry_add_ac... 2019-02-05 20:23:21.564834
6 contenttypes 0002_remove_content_... 2019-02-05 20:23:21.597186
7 auth 0002_alter_permissio... 2019-02-05 20:23:21.608705
8 auth 0003_alter_user_emai... 2019-02-05 20:23:21.628441
9 auth 0004_alter_user_user... 2019-02-05 20:23:21.646824
10 auth 0005_alter_user_last... 2019-02-05 20:23:21.661182
11 auth 0006_require_content... 2019-02-05 20:23:21.663664
12 auth 0007_alter_validator... 2019-02-05 20:23:21.679482
13 auth 0008_alter_user_user... 2019-02-05 20:23:21.699201
14 auth 0009_alter_user_last... 2019-02-05 20:23:21.718652
15 historical_data 0001_initial 2019-02-05 20:23:21.726000
16 sessions 0001_initial 2019-02-05 20:23:21.734611
19 historical_data 0002_switch_to_decimals 2019-02-05 20:30:11.337894

Както можете да видите, има запис за всяка приложена миграция. Таблицата не само съдържа миграциите от нашите historical_data приложение, но също и миграциите от всички други инсталирани приложения.

Следващият път, когато миграциите се изпълняват, Django ще пропусне миграциите, изброени в таблицата на базата данни. Това означава, че дори ако промените ръчно файла на вече приложена миграция, Django ще игнорира тези промени, стига вече да има запис за него в базата данни.

Можете да подмамите Django да изпълни повторно миграция, като изтриете съответния ред от таблицата, но това рядко е добра идея и може да ви остави с повредена система за миграция.



Файлът за миграция

Какво се случва, когато стартирате python manage.py makemigrations <appname> ? Django търси промени, направени в моделите във вашето приложение <appname> . Ако намери такъв, като модел, който е добавен, тогава създава файл за миграция в migrations поддиректория. Този файл за мигриране съдържа списък с операции за синхронизиране на вашата схема на база данни с дефиницията на вашия модел.

Забележка: Приложението ви трябва да бъде посочено в INSTALLED_APPS настройка и трябва да съдържа migrations директория с __init__.py файл. В противен случай Django няма да създаде никакви миграции за него.

migrations директорията се създава автоматично, когато създадете ново приложение с startapp команда за управление, но е лесно да се забрави, когато създавате приложение ръчно.

Файловете за миграция са просто Python, така че нека да разгледаме първия файл за миграция в historical_prices ап. Можете да го намерите на historical_prices/migrations/0001_initial.py . Трябва да изглежда така:

from django.db import models, migrations

class Migration(migrations.Migration):
    dependencies = []
    operations = [
        migrations.CreateModel(
            name='PriceHistory',
            fields=[
                ('id', models.AutoField(
                    verbose_name='ID',
                    serialize=False,
                    primary_key=True,
                    auto_created=True)),
                ('date', models.DateTimeField(auto_now_add=True)),
                ('price', models.DecimalField(decimal_places=2, max_digits=5)),
                ('volume', models.PositiveIntegerField()),
                ('total_btc', models.PositiveIntegerField()),
            ],
            options={
            },
            bases=(models.Model,),
        ),
    ]

Както можете да видите, той съдържа един клас, наречен Migration който наследява от django.db.migrations.Migration . Това е класът, който рамката за мигриране ще търси и изпълнява, когато го помолите да приложи миграции.

Migration клас съдържа два основни списъка:

  1. dependencies
  2. operations

Операции по миграция

Нека разгледаме operations списък първо. Тази таблица съдържа операциите, които трябва да се извършат като част от миграцията. Операциите са подкласове на класа django.db.migrations.operations.base.Operation . Ето общите операции, които са вградени в Django:

Операционен клас Описание
CreateModel Създава нов модел и съответната таблица на базата данни
DeleteModel Изтрива модел и изтрива таблицата на базата данни
RenameModel Преименува модел и преименува неговата таблица на база данни
AlterModelTable Преименува таблицата на базата данни за модел
AlterUniqueTogether Променя уникалните ограничения на модел
AlterIndexTogether Променя индексите на модел
AlterOrderWithRespectTo Създава или изтрива _order колона за модел
AlterModelOptions Променя различни опции на модела, без да засяга базата данни
AlterModelManagers Променя наличните мениджъри по време на миграции
AddField Добавя поле към модел и съответната колона в базата данни
RemoveField Премахва поле от модел и премахва съответната колона от базата данни
AlterField Променя дефиницията на поле и променя колоната на базата данни, ако е необходимо
RenameField Преименува поле и, ако е необходимо, неговата колона от база данни
AddIndex Създава индекс в таблицата на базата данни за модела
RemoveIndex Премахва индекс от таблицата на базата данни за модела

Обърнете внимание как операциите са наименувани след промени, направени в дефинициите на модела, а не действията, които се извършват в базата данни. Когато приложите миграция, всяка операция е отговорна за генерирането на необходимите SQL изрази за вашата конкретна база данни. Например, CreateModel ще генерира CREATE TABLE SQL израз.

Извън кутията миграциите имат поддръжка за всички стандартни бази данни, които Django поддържа. Така че, ако се придържате към операциите, изброени тук, тогава можете да правите повече или по-малко всякакви промени във вашите модели, които искате, без да се притеснявате за основния SQL. Всичко е направено за вас.

Забележка: В някои случаи Django може да не открие правилно промените ви. Ако преименувате модел и промените няколко от полетата му, Django може да сбърка това с нов модел.

Вместо RenameModel и няколко AlterField операции, той ще създаде DeleteModel и CreateModel операция. Вместо да преименува таблицата на базата данни за модела, тя ще я изпусне и ще създаде нова таблица с новото име, като ефективно ще изтрие всичките ви данни!

Създайте си навик да проверявате генерираните миграции и да ги тествате върху копие на вашата база данни, преди да ги стартирате върху производствени данни.

Django предоставя още три операционни класа за разширени случаи на употреба:

  1. RunSQL ви позволява да изпълнявате персонализиран SQL в базата данни.
  2. RunPython ви позволява да изпълнявате всеки код на Python.
  3. SeparateDatabaseAndState е специализирана операция за напреднали приложения.

С тези операции можете основно да правите всякакви промени, които искате във вашата база данни. Въпреки това, няма да намерите тези операции в миграция, която е създадена автоматично с makemigrations команда за управление.

След Django 2.0 има и няколко специфични за PostgreSQL операции, налични в django.contrib.postgres.operations които можете да използвате за инсталиране на различни разширения на PostgreSQL:

  • BtreeGinExtension
  • BtreeGistExtension
  • CITextExtension
  • CryptoExtension
  • HStoreExtension
  • TrigramExtension
  • UnaccentExtension

Имайте предвид, че миграция, съдържаща една от тези операции, изисква потребител на база данни с привилегии на суперпотребител.

Не на последно място, можете също да създадете свои собствени операционни класове. Ако искате да разгледате това, тогава разгледайте документацията на Django за създаване на персонализирани операции за миграция.



Зависимости от миграция

dependencies списъкът в клас за мигриране съдържа всички миграции, които трябва да бъдат приложени, преди да може да се приложи тази миграция.

В 0001_initial.py миграция, която видяхте по-горе, нищо не трябва да се прилага предварително, така че да няма зависимости. Нека да разгледаме втората миграция в historical_prices ап. Във файла 0002_switch_to_decimals.py , dependencies атрибут на Migration има запис:

from django.db import migrations, models

class Migration(migrations.Migration):
    dependencies = [
        ('historical_data', '0001_initial'),
    ]
    operations = [
        migrations.AlterField(
            model_name='pricehistory',
            name='volume',
            field=models.DecimalField(decimal_places=3, max_digits=7),
        ),
    ]

Зависимостта по-горе казва, че миграцията 0001_initial на приложението historical_data първо трябва да се стартира. Това има смисъл, тъй като миграцията 0001_initial създава таблицата, съдържаща полето, което миграцията 0002_switch_to_decimals иска да се промени.

Една миграция може също да има зависимост от миграция от друго приложение, като това:

class Migration(migrations.Migration):
    ...

    dependencies = [
        ('auth', '0009_alter_user_last_name_max_length'),
    ]

Това обикновено е необходимо, ако моделът има външен ключ, насочващ към модел в друго приложение.

Като алтернатива можете също да наложите миграцията да се изпълнява преди друга миграция с помощта на атрибута run_before :

class Migration(migrations.Migration):
    ...

    run_before = [
        ('third_party_app', '0001_initial'),
    ]

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

Опцията за изрично дефиниране на зависимости между миграциите също означава, че номерирането на миграциите (обикновено 0001 , 0002 , 0003 , ...) не представлява строго реда, в който се прилагат миграциите. Можете да добавите всяка зависимост, която искате, и по този начин да контролирате реда, без да се налага да преномерирате всички миграции.



Преглед на миграцията

По принцип не е нужно да се притеснявате за SQL, който генерират миграциите. Но ако искате да проверите отново дали генерираният SQL има смисъл или просто сте любопитни как изглежда, тогава Django ви е покрил с sqlmigrate команда за управление:

$ python manage.py sqlmigrate historical_data 0001
BEGIN;
--
-- Create model PriceHistory
--
CREATE TABLE "historical_data_pricehistory" (
    "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
    "date" datetime NOT NULL,
    "price" decimal NOT NULL,
    "volume" integer unsigned NOT NULL
);
COMMIT;

Това ще изброи основните SQL заявки, които ще бъдат генерирани от посочената миграция въз основа на базата данни във вашия settings.py файл. Когато предадете параметъра --backwards , Django генерира SQL, за да отмени миграцията:

$ python manage.py sqlmigrate --backwards historical_data 0001
BEGIN;
--
-- Create model PriceHistory
--
DROP TABLE "historical_data_pricehistory";
COMMIT;

След като видите изхода на sqlmigrate за малко по-сложна миграция може да оцените, че не е нужно да изработвате целия този SQL на ръка!




Как Django открива промени във вашите модели

Видяхте как изглежда файлът за миграция и как е неговият списък с Operation classes дефинира извършените промени в базата данни. Но как точно Django знае кои операции трябва да влязат във файл за миграция? Може да очаквате, че Django сравнява вашите модели със схемата на вашата база данни, но това не е така.

Когато изпълнявате makemigrations , Django не проверете вашата база данни. Нито сравнява файла на вашия модел с по-ранна версия. Вместо това Django преминава през всички миграции, които са били приложени, и изгражда състояние на проекта за това как трябва да изглеждат моделите. След това това състояние на проекта се сравнява с текущите дефиниции на модела и се създава списък с операции, който, когато се приложи, ще актуализира състоянието на проекта с дефинициите на модела.


Игра на шах с Django

Можете да мислите за моделите си като за шахматна дъска, а Джанго е гросмайстор на шах, който ви наблюдава как играете срещу себе си. Но гросмайсторът не следи всяко твое движение. Грандмайсторът поглежда към дъската само когато крещиш makemigrations .

Тъй като има само ограничен набор от възможни ходове (а гросмайсторът е гросмайстор), тя може да измисли ходовете, които са се случили, откакто за последно е погледнала дъската. Тя си прави бележки и ви позволява да играете, докато не извикате makemigrations отново.

Когато гледа дъската следващия път, гросмайсторът не си спомня как е изглеждала шахматната дъска последния път, но може да прегледа бележките си за предишните ходове и да изгради мислен модел на това как е изглеждала шахматната дъска.

Сега, когато извикате migrate , гросмайсторът ще изиграе отново всички записани ходове на друга шахматна дъска и ще отбележи в електронна таблица кои от нейните записи вече са приложени. Тази втора шахматна дъска е вашата база данни, а електронната таблица е django_migrations таблица.

Тази аналогия е доста подходяща, защото илюстрира добре някои поведения на Django миграции:

  • Джанго миграциите се опитват да бъдат ефективни: Точно както гросмайсторът предполага, че сте направили най-малък брой ходове, Django ще се опита да създаде най-ефективните миграции. Ако добавите поле с име A към модел, след което го преименувайте на B , и след това стартирайте makemigrations , тогава Django ще създаде нова миграция, за да добави поле с име B .

  • Джанго миграциите имат своите ограничения: Ако направите много ходове, преди да оставите гросмайстора да погледне шахматната дъска, тогава тя може да не е в състояние да проследи точните движения на всяка фигура. По същия начин Django може да не измисли правилната миграция, ако направите твърде много промени наведнъж.

  • Django migration очаква да играете по правилата: Когато направите нещо неочаквано, като например да вземете произволна фигура от дъската или да бъркате с нотите, гросмайсторът може да не забележи в началото, но рано или късно тя ще вдигне ръце и ще откаже да продължи. Същото се случва, когато се забърквате с django_migrations таблица или променете вашата схема на база данни извън миграцията, например като изтриете таблицата на базата данни за модел.



Разбиране на SeparateDatabaseAndState

Сега, когато знаете за състоянието на проекта, което Django изгражда, е време да разгледаме по-отблизо операцията SeparateDatabaseAndState . Тази операция може да направи точно това, което подсказва името:може да отдели състоянието на проекта (менталният модел, който Django изгражда) от вашата база данни.

SeparateDatabaseAndState се инстанцира с два списъка с операции:

  1. state_operations съдържа операции, които се прилагат само към състоянието на проекта.
  2. database_operations съдържа операции, които се прилагат само към базата данни.

Тази операция ви позволява да правите всякакъв вид промяна във вашата база данни, но ваша отговорност е да се уверите, че състоянието на проекта отговаря на базата данни след това. Примерни случаи на употреба за SeparateDatabaseAndState премествате модел от едно приложение в друго или създавате индекс в огромна база данни без прекъсвания.

SeparateDatabaseAndState е усъвършенствана операция и няма да имате нужда от работа с миграции през първия си ден, а може би изобщо никога. SeparateDatabaseAndState е подобно на сърдечната хирургия. Това носи доста голям риск и не е нещо, което правите просто за забавление, но понякога е необходима процедура, за да поддържате пациента жив.




Заключение

Това завършва вашето дълбоко потапяне в миграциите на Django. Честито! Покрихте доста напреднали теми и вече имате солидно разбиране какво се случва под капака на миграцията.

Научихте, че:

  • Django следи приложените миграции в таблицата за миграции на Django.
  • Джанго миграциите се състоят от обикновени Python файлове, съдържащи Migration клас.
  • Django знае кои промени да извърши от operations списък в Migration класове.
  • Django сравнява вашите модели със състояние на проекта, което изгражда от миграциите.

С тези знания вече сте готови да се заемете с третата част от поредицата за миграциите на Django, където ще научите как да използвате миграциите на данни, за да правите безопасно еднократни промени в данните си. Останете на линия!

Тази статия използва bitcoin_tracker Проект Django, изграден в Django Migrations:A Primer. Можете или да създадете отново този проект, като работите през тази статия, или можете да изтеглите изходния код:

Изтеглете изходния код: Щракнете тук, за да изтеглите кода за проекта за миграции на Django, който ще използвате в тази статия.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Предимства и сигурност в услугата за релационна база данни на Amazon

  2. Намиране на предимства на производителността с разделяне

  3. Пространството за таблици SYSMMGMTDATA е ПЪЛНО в хранилището за управление на грид инфраструктурата (MGMTDB)

  4. Компресия и нейните ефекти върху производителността

  5. Извършване на одит на промените в данните с помощта на временна таблица