Database
 sql >> Baza danych >  >> RDS >> Database

Zagłębianie się w migracje Django

To jest drugi artykuł z naszej serii migracji Django:

  • Część 1:Migracje Django:elementarz
  • Część 2:Zagłębianie się w migracje Django (aktualny artykuł)
  • Część 3:Migracje danych
  • Wideo:Migracje Django 1.7 – elementarz

W poprzednim artykule z tej serii dowiedziałeś się o celu migracji Django. Zapoznałeś się z podstawowymi wzorcami użytkowania, takimi jak tworzenie i stosowanie migracji. Teraz nadszedł czas, aby zagłębić się w system migracji i rzucić okiem na niektóre z jego podstawowych mechanizmów.

Pod koniec tego artykułu będziesz wiedzieć:

  • Jak Django śledzi migracje
  • Skąd migracje wiedzą, jakie operacje na bazie danych wykonać
  • Jak definiowane są zależności między migracjami

Gdy już opanujesz tę część systemu migracji Django, będziesz dobrze przygotowany do tworzenia własnych, niestandardowych migracji. Przejdźmy dokładnie tam, gdzie skończyliśmy!

Ten artykuł wykorzystuje bitcoin_tracker Projekt Django zbudowany w Django Migrations:A Primer. Możesz albo odtworzyć ten projekt, pracując nad tym artykułem, albo pobrać kod źródłowy:

Pobierz kod źródłowy: Kliknij tutaj, aby pobrać kod projektu migracji Django, którego będziesz używać w tym artykule.


Skąd Django wie, które migracje należy zastosować

Podsumujmy ostatni krok poprzedniego artykułu z tej serii. Utworzyłeś migrację, a następnie zastosowałeś wszystkie dostępne migracje za pomocą python manage.py migrate .Jeśli to polecenie zadziałało pomyślnie, tabele bazy danych odpowiadają teraz definicjom modelu.

Co się stanie, jeśli ponownie uruchomisz to polecenie? Wypróbujmy to:

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

Nic się nie stało! Gdy migracja zostanie zastosowana do bazy danych, Django nie zastosuje tej migracji ponownie do tej konkretnej bazy danych. Zapewnienie, że migracja zostanie zastosowana tylko raz, wymaga śledzenia zastosowanych migracji.

Django używa tabeli bazy danych o nazwie django_migrations . Django automatycznie tworzy tę tabelę w Twojej bazie danych przy pierwszym zastosowaniu jakichkolwiek migracji. W przypadku każdej zastosowanej lub sfałszowanej migracji do tabeli wstawiany jest nowy wiersz.

Na przykład, oto jak wygląda ta tabela w naszym bitcoin_tracker projekt:

ID Aplikacja Nazwa Zastosowano
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

Jak widać, dla każdej zastosowanej migracji istnieje wpis. Tabela zawiera nie tylko migracje z naszych historical_data aplikacji, ale także migracje ze wszystkich innych zainstalowanych aplikacji.

Przy następnym uruchomieniu migracji Django pominie migracje wymienione w tabeli bazy danych. Oznacza to, że nawet jeśli ręcznie zmienisz plik migracji, która została już zastosowana, Django zignoruje te zmiany, o ile istnieje już wpis w bazie danych.

Możesz nakłonić Django do ponownego uruchomienia migracji, usuwając odpowiedni wiersz z tabeli, ale rzadko jest to dobry pomysł i może spowodować uszkodzenie systemu migracji.



Plik migracji

Co się stanie, gdy uruchomisz python manage.py makemigrations <appname> ? Django szuka zmian wprowadzonych w modelach w Twojej aplikacji <appname> . Jeśli znajdzie jakieś, takie jak dodany model, tworzy plik migracji w migrations podkatalog. Ten plik migracji zawiera listę operacji mających na celu zsynchronizowanie schematu bazy danych z definicją modelu.

Uwaga: Twoja aplikacja musi być wymieniona w INSTALLED_APPS ustawienie i musi zawierać migrations katalog z __init__.py plik. W przeciwnym razie Django nie utworzy dla niego żadnych migracji.

migrations katalog jest tworzony automatycznie podczas tworzenia nowej aplikacji za pomocą startapp polecenie zarządzania, ale łatwo o tym zapomnieć podczas ręcznego tworzenia aplikacji.

Pliki migracji to tylko Python, więc spójrzmy na pierwszy plik migracji w historical_prices aplikacja. Możesz go znaleźć pod adresem historical_prices/migrations/0001_initial.py . Powinno to wyglądać mniej więcej tak:

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,),
        ),
    ]

Jak widać, zawiera jedną klasę o nazwie Migration który dziedziczy z django.db.migrations.Migration . Jest to klasa, której framework migracji będzie szukał i wykonywał, gdy poprosisz go o zastosowanie migracji.

Migration klasa zawiera dwie główne listy:

  1. dependencies
  2. operations

Operacje migracyjne

Spójrzmy na operations lista w pierwszej kolejności. Ta tabela zawiera operacje, które mają zostać wykonane w ramach migracji. Operacje są podklasami klasy django.db.migrations.operations.base.Operation . Oto typowe operacje wbudowane w Django:

Klasa operacji Opis
CreateModel Tworzy nowy model i odpowiednią tabelę bazy danych
DeleteModel Usuwa model i upuszcza jego tabelę bazy danych
RenameModel Zmienia nazwę modelu i jego tabeli bazy danych
AlterModelTable Zmienia nazwę tabeli bazy danych dla modelu
AlterUniqueTogether Zmienia unikalne ograniczenia modelu
AlterIndexTogether Zmienia indeksy modelu
AlterOrderWithRespectTo Tworzy lub usuwa _order kolumna dla modelu
AlterModelOptions Zmienia różne opcje modelu bez wpływu na bazę danych
AlterModelManagers Zmienia menedżerów dostępnych podczas migracji
AddField Dodaje pole do modelu i odpowiednią kolumnę w bazie danych
RemoveField Usuwa pole z modelu i usuwa odpowiednią kolumnę z bazy danych
AlterField Zmienia definicję pola i w razie potrzeby zmienia kolumnę bazy danych
RenameField Zmienia nazwę pola i, jeśli to konieczne, również jego kolumny bazy danych
AddIndex Tworzy indeks w tabeli bazy danych dla modelu
RemoveIndex Usuwa indeks z tabeli bazy danych dla modelu

Zwróć uwagę, jak operacje są nazywane po zmianach wprowadzonych w definicjach modelu, a nie po akcjach wykonywanych w bazie danych. Po zastosowaniu migracji każda operacja jest odpowiedzialna za wygenerowanie niezbędnych instrukcji SQL dla określonej bazy danych. Na przykład CreateModel wygeneruje CREATE TABLE Instrukcja SQL.

Po wyjęciu z pudełka migracje obsługują wszystkie standardowe bazy danych obsługiwane przez Django. Jeśli więc będziesz trzymać się wymienionych tutaj operacji, możesz dokonać mniej więcej dowolnych zmian w swoich modelach, nie martwiąc się o bazowy kod SQL. To wszystko za Ciebie.

Uwaga: W niektórych przypadkach Django może nie wykryć poprawnie wprowadzonych zmian. Jeśli zmienisz nazwę modelu i zmienisz kilka jego pól, Django może pomylić to z nowym modelem.

Zamiast RenameModel i kilka AlterField operacji, utworzy DeleteModel i CreateModel operacja. Zamiast zmieniać nazwę tabeli bazy danych dla modelu, usunie ją i utworzy nową tabelę o nowej nazwie, skutecznie usuwając wszystkie dane!

Wyrób sobie nawyk sprawdzania wygenerowanych migracji i testowania ich na kopii bazy danych przed uruchomieniem ich na danych produkcyjnych.

Django zapewnia trzy dodatkowe klasy operacyjne dla zaawansowanych przypadków użycia:

  1. RunSQL pozwala na uruchomienie niestandardowego SQL w bazie danych.
  2. RunPython pozwala uruchomić dowolny kod Pythona.
  3. SeparateDatabaseAndState to wyspecjalizowana operacja do zaawansowanych zastosowań.

Dzięki tym operacjom możesz w zasadzie dokonywać dowolnych zmian w swojej bazie danych. Jednak nie znajdziesz tych operacji w migracji, która została utworzona automatycznie za pomocą makemigrations polecenie zarządzania.

Od Django 2.0 istnieje również kilka operacji specyficznych dla PostgreSQL dostępnych w django.contrib.postgres.operations którego możesz użyć do zainstalowania różnych rozszerzeń PostgreSQL:

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

Pamiętaj, że migracja zawierająca jedną z tych operacji wymaga użytkownika bazy danych z uprawnieniami superużytkownika.

Last but not least, możesz także tworzyć własne klasy operacyjne. Jeśli chcesz się temu przyjrzeć, spójrz na dokumentację Django na temat tworzenia niestandardowych operacji migracji.



Zależności migracji

dependencies lista w klasie migracji zawiera wszelkie migracje, które muszą zostać zastosowane przed zastosowaniem tej migracji.

W 0001_initial.py migracja, którą widziałeś powyżej, nic nie musi być stosowane wcześniej, więc nie ma żadnych zależności. Rzućmy okiem na drugą migrację w historical_prices aplikacja. W pliku 0002_switch_to_decimals.py , dependencies atrybut Migration ma wpis:

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),
        ),
    ]

Powyższa zależność mówi, że migracja 0001_initial aplikacji historical_data musi być uruchomiony jako pierwszy. To ma sens, ponieważ migracja 0001_initial tworzy tabelę zawierającą pole, które migracja 0002_switch_to_decimals chce się zmienić.

Migracja może również zależeć od migracji z innej aplikacji, na przykład:

class Migration(migrations.Migration):
    ...

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

Jest to zwykle konieczne, jeśli model ma klucz obcy wskazujący na model w innej aplikacji.

Możesz też wymusić uruchomienie migracji przed kolejna migracja przy użyciu atrybutu run_before :

class Migration(migrations.Migration):
    ...

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

Zależności można również łączyć, dzięki czemu można mieć wiele zależności. Ta funkcja zapewnia dużą elastyczność, ponieważ możesz dostosować klucze obce, które zależą od modeli z różnych aplikacji.

Opcja jawnego zdefiniowania zależności między migracjami oznacza również, że numeracja migracji (zwykle 0001 , 0002 , 0003 , …) nie odzwierciedla ściśle kolejności stosowania migracji. Możesz dodać dowolną zależność, a tym samym kontrolować kolejność bez konieczności ponownego numerowania wszystkich migracji.



Wyświetlanie migracji

Generalnie nie musisz się martwić o kod SQL generowany przez migracje. Ale jeśli chcesz dwukrotnie sprawdzić, czy wygenerowany SQL ma sens lub jesteś po prostu ciekaw, jak wygląda, to Django zapewni Ci sqlmigrate polecenie zarządzania:

$ 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;

Spowoduje to wyświetlenie listy bazowych zapytań SQL, które zostaną wygenerowane przez określoną migrację, na podstawie bazy danych w pliku settings.py plik. Po przekazaniu parametru --backwards , Django generuje kod SQL, aby anulować migrację:

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

Gdy zobaczysz wynik sqlmigrate w przypadku nieco bardziej złożonej migracji możesz docenić fakt, że nie musisz ręcznie tworzyć całego tego kodu SQL!




Jak Django wykrywa zmiany w Twoich modelach

Widziałeś, jak wygląda plik migracji i jak jego lista Operation klasy definiują zmiany dokonywane w bazie danych. Ale skąd dokładnie Django wie, które operacje powinny znaleźć się w pliku migracji? Możesz się spodziewać, że Django porównuje twoje modele ze schematem bazy danych, ale tak nie jest.

Podczas uruchamiania makemigrations , Django nie sprawdź swoją bazę danych. Nie porównuje też pliku modelu z wcześniejszą wersją. Zamiast tego Django przechodzi przez wszystkie zastosowane migracje i buduje stan projektu, jak powinny wyglądać modele. Ten stan projektu jest następnie porównywany z obecnymi definicjami modeli i tworzona jest lista operacji, które po zastosowaniu uaktualnią stan projektu z definicjami modeli.


Gra w szachy z Django

Możesz myśleć o swoich modelach jak o szachownicy, a Django jest arcymistrzem szachowym, obserwującym, jak grasz przeciwko sobie. Ale arcymistrz nie obserwuje każdego twojego ruchu. Arcymistrz patrzy na tablicę tylko wtedy, gdy krzyczysz makemigrations .

Ponieważ istnieje tylko ograniczony zestaw możliwych ruchów (a arcymistrz jest arcymistrzem), może wymyślić ruchy, które miały miejsce od ostatniego spojrzenia na planszę. Robi notatki i pozwala ci grać, dopóki nie krzykniesz makemigrations ponownie.

Gdy następnym razem patrzy na szachownicę, arcymistrz nie pamięta, jak wyglądała szachownica ostatnim razem, ale może przejrzeć swoje notatki z poprzednich ruchów i zbudować mentalny model tego, jak wyglądała szachownica.

Teraz, kiedy krzyczysz migrate , arcymistrz odtworzy wszystkie zarejestrowane ruchy na innej szachownicy i zapisze w arkuszu kalkulacyjnym, które z jej zapisów zostały już zastosowane. Ta druga szachownica to twoja baza danych, a arkusz kalkulacyjny to django_migrations tabela.

Ta analogia jest całkiem trafna, ponieważ dobrze ilustruje niektóre zachowania migracji Django:

  • Migracje Django starają się być wydajne: Tak jak arcymistrz zakłada, że ​​wykonałeś najmniej ruchów, tak Django postara się stworzyć najbardziej wydajne migracje. Jeśli dodasz pole o nazwie A do modelu, a następnie zmień jego nazwę na B , a następnie uruchom makemigrations , wtedy Django utworzy nową migrację, aby dodać pole o nazwie B .

  • Migracje Django mają swoje ograniczenia: Jeśli wykonasz wiele ruchów, zanim pozwolisz arcymistrzowi spojrzeć na szachownicę, może nie być w stanie odtworzyć dokładnych ruchów każdego pionka. Podobnie, Django może nie wymyślić poprawnej migracji, jeśli wprowadzisz zbyt wiele zmian naraz.

  • Migracja Django oczekuje, że będziesz grać zgodnie z zasadami: Kiedy zrobisz coś nieoczekiwanego, jak na przykład zdejmowanie losowego pionka z planszy lub grzebanie w notatkach, arcymistrz może początkowo tego nie zauważyć, ale prędzej czy później podniesie ręce i odmówi kontynuowania. To samo dzieje się, gdy zadzierasz z django_migrations tabeli lub zmień schemat bazy danych poza migracją, na przykład usuwając tabelę bazy danych dla modelu.



Zrozumienie SeparateDatabaseAndState

Teraz, gdy wiesz już o stanie projektu, który buduje Django, nadszedł czas, aby przyjrzeć się operacji SeparateDatabaseAndState . Ta operacja może zrobić dokładnie to, co sugeruje nazwa:może oddzielić stan projektu (model mentalny budowany przez Django) od bazy danych.

SeparateDatabaseAndState jest tworzony z dwiema listami operacji:

  1. state_operations zawiera operacje, które są stosowane tylko do stanu projektu.
  2. database_operations zawiera operacje, które są stosowane tylko do bazy danych.

Ta operacja pozwala wprowadzić dowolne zmiany w bazie danych, ale Twoim obowiązkiem jest upewnienie się, że stan projektu będzie później pasował do bazy danych. Przykładowe przypadki użycia dla SeparateDatabaseAndState przenosisz model z jednej aplikacji do drugiej lub tworzysz indeks w ogromnej bazie danych bez przestojów.

SeparateDatabaseAndState to zaawansowana operacja i nie będziesz potrzebować pierwszego dnia pracy z migracjami, a może wcale. SeparateDatabaseAndState jest podobny do operacji serca. Wiąże się to z pewnym ryzykiem i nie jest czymś, co robi się tylko dla zabawy, ale czasami jest to niezbędna procedura, aby utrzymać pacjenta przy życiu.




Wniosek

To kończy twoje głębokie zanurzenie w migracjach Django. Gratulacje! Omówiłeś sporo zaawansowanych tematów i teraz dobrze rozumiesz, co dzieje się pod maską migracji.

Dowiedziałeś się, że:

  • Django śledzi zastosowane migracje w tabeli migracji Django.
  • Migracje Django składają się ze zwykłych plików Pythona zawierających element Migration klasa.
  • Django wie, jakie zmiany wykonać z operations lista w Migration zajęcia.
  • Django porównuje Twoje modele ze stanem projektu, który buduje na podstawie migracji.

Mając tę ​​wiedzę, jesteś gotowy, aby zająć się trzecią częścią serii o migracjach Django, w której dowiesz się, jak używać migracji danych, aby bezpiecznie wprowadzać jednorazowe zmiany w swoich danych. Bądź na bieżąco!

W tym artykule wykorzystano bitcoin_tracker Projekt Django zbudowany w Django Migrations:A Primer. Możesz albo odtworzyć ten projekt, pracując nad tym artykułem, albo pobrać kod źródłowy:

Pobierz kod źródłowy: Kliknij tutaj, aby pobrać kod projektu migracji Django, którego będziesz używać w tym artykule.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Identyfikowanie i naprawianie problemu z wydajnością przekazanych rekordów

  2. Projektowanie baz danych z Vertabelo

  3. Jak utworzyć jedną tabelę z innej tabeli w SQL?

  4. Co mają wspólnego igrzyska olimpijskie, mecze piłkarskie UEFA Euro 2016 i bazy danych?

  5. GROUP BY vs ORDER BY