sql >> Database teknologi >  >> RDS >> Database

Graver dybere ind i Django-migreringer

Dette er den anden artikel i vores Django-migreringsserie:

  • Del 1:Django Migrations:A Primer
  • Del 2:Grave dybere ned i Django-migreringer (aktuel artikel)
  • Del 3:Datamigrering
  • Video:Django 1.7 Migrations - A Primer

I den forrige artikel i denne serie lærte du om formålet med Django-migreringer. Du er blevet fortrolig med grundlæggende brugsmønstre som at oprette og anvende migreringer. Nu er det tid til at grave dybere ned i migrationssystemet og tage et kig på nogle af dets underliggende mekanikker.

I slutningen af ​​denne artikel ved du:

  • Hvordan Django holder styr på migreringer
  • Hvordan migreringer ved, hvilke databasehandlinger der skal udføres
  • Hvordan afhængigheder mellem migreringer defineres

Når du har viklet dit hoved omkring denne del af Django-migreringssystemet, vil du være godt forberedt til at oprette dine egne tilpassede migreringer. Lad os springe lige ind, hvor vi slap!

Denne artikel bruger bitcoin_tracker Django-projekt bygget i Django Migrations:A Primer. Du kan enten genskabe det pågældende projekt ved at arbejde gennem artiklen, eller du kan downloade kildekoden:

Download kildekode: Klik her for at downloade koden til Django-migreringsprojektet, du vil bruge i denne artikel.


Hvordan Django ved, hvilke migreringer der skal anvendes

Lad os opsummere det allersidste trin i den forrige artikel i serien. Du oprettede en migrering og anvendte derefter alle tilgængelige migreringer med python manage.py migrate .Hvis den kommando kørte med succes, så matcher dine databasetabeller nu din models definitioner.

Hvad sker der, hvis du kører den kommando igen? Lad os prøve det:

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

Intet skete! Når først en migrering er blevet anvendt til en database, vil Django ikke anvende denne migrering til den pågældende database igen. At sikre, at en migrering kun anvendes én gang, kræver, at man holder styr på de migreringer, der er blevet anvendt.

Django bruger en databasetabel kaldet django_migrations . Django opretter automatisk denne tabel i din database, første gang du anvender nogen migreringer. For hver migrering, der er anvendt eller forfalsket, indsættes en ny række i tabellen.

For eksempel, her er, hvordan denne tabel ser ud i vores bitcoin_tracker projekt:

ID App Navn Anvendt
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

Som du kan se, er der en post for hver anvendt migrering. Tabellen indeholder ikke kun migreringerne fra vores historical_data app, men også migreringerne fra alle andre installerede apps.

Næste gang migreringer køres, vil Django springe over de migreringer, der er angivet i databasetabellen. Dette betyder, at selvom du manuelt ændrer filen for en migrering, der allerede er blevet anvendt, vil Django ignorere disse ændringer, så længe der allerede er en post for den i databasen.

Du kan narre Django til at køre en migrering igen ved at slette den tilsvarende række fra tabellen, men det er sjældent en god idé og kan efterlade dig med et ødelagt migreringssystem.



Migreringsfilen

Hvad sker der, når du kører python manage.py makemigrations <appname> ? Django leder efter ændringer, der er foretaget på modellerne i din app <appname> . Hvis den finder nogen, f.eks. en model, der er blevet tilføjet, opretter den en migrationsfil i migrations undermappe. Denne migreringsfil indeholder en liste over handlinger for at bringe dit databaseskema i synkronisering med din modeldefinition.

Bemærk: Din app skal være opført i INSTALLED_APPS indstilling, og den skal indeholde en migrations mappe med en __init__.py fil. Ellers vil Django ikke oprette nogen migreringer til det.

migrations bibliotek oprettes automatisk, når du opretter en ny app med startapp ledelseskommando, men det er nemt at glemme, når du opretter en app manuelt.

Migrationsfilerne er kun Python, så lad os tage et kig på den første migreringsfil i historical_prices app. Du kan finde den på historical_prices/migrations/0001_initial.py . Det skulle se sådan ud:

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

Som du kan se, indeholder den en enkelt klasse kaldet Migration som arver fra django.db.migrations.Migration . Dette er den klasse, som migreringsrammen vil lede efter og udføre, når du beder den om at anvende migreringer.

Migration klasse indeholder to hovedlister:

  1. dependencies
  2. operations

Migreringsoperationer

Lad os se på operations liste først. Denne tabel indeholder de operationer, der skal udføres som en del af migreringen. Operationer er underklasser af klassen django.db.migrations.operations.base.Operation . Her er de almindelige operationer, der er indbygget i Django:

Operation Class Beskrivelse
CreateModel Opretter en ny model og den tilsvarende databasetabel
DeleteModel Sletter en model og sletter dens databasetabel
RenameModel Omdøber en model og omdøber dens databasetabel
AlterModelTable Omdøber databasetabellen for en model
AlterUniqueTogether Ændrer de unikke begrænsninger for en model
AlterIndexTogether Ændrer indeksene for en model
AlterOrderWithRespectTo Opretter eller sletter _order kolonne for en model
AlterModelOptions Ændrer forskellige modelindstillinger uden at påvirke databasen
AlterModelManagers Ændrer de tilgængelige administratorer under migreringer
AddField Føjer et felt til en model og den tilsvarende kolonne i databasen
RemoveField Fjerner et felt fra en model og sletter den tilsvarende kolonne fra databasen
AlterField Ændrer et felts definition og ændrer dets databasekolonne, hvis det er nødvendigt
RenameField Omdøber et felt og om nødvendigt også dets databasekolonne
AddIndex Opretter et indeks i databasetabellen for modellen
RemoveIndex Fjerner et indeks fra databasetabellen for modellen

Bemærk, hvordan operationerne er opkaldt efter ændringer, der er foretaget i modeldefinitioner, ikke de handlinger, der udføres på databasen. Når du anvender en migrering, er hver operation ansvarlig for at generere de nødvendige SQL-sætninger til din specifikke database. For eksempel CreateModel ville generere en CREATE TABLE SQL-sætning.

Ud af boksen har migreringer understøttelse af alle de standarddatabaser, som Django understøtter. Så hvis du holder dig til de operationer, der er angivet her, så kan du foretage mere eller mindre de ændringer af dine modeller, som du ønsker, uden at skulle bekymre dig om den underliggende SQL. Det er alt sammen gjort for dig.

Bemærk: I nogle tilfælde kan Django muligvis ikke registrere dine ændringer korrekt. Hvis du omdøber en model og ændrer flere af dens felter, kan Django forveksle dette med en ny model.

I stedet for en RenameModel og flere AlterField operationer, vil det skabe en DeleteModel og en CreateModel operation. I stedet for at omdøbe databasetabellen for modellen, vil den droppe den og oprette en ny tabel med det nye navn, hvilket effektivt sletter alle dine data!

Gør det til en vane at tjekke de genererede migreringer og teste dem på en kopi af din database, før du kører dem på produktionsdata.

Django leverer yderligere tre operationsklasser til avancerede anvendelsestilfælde:

  1. RunSQL giver dig mulighed for at køre tilpasset SQL i databasen.
  2. RunPython giver dig mulighed for at køre enhver Python-kode.
  3. SeparateDatabaseAndState er en specialiseret operation til avanceret brug.

Med disse operationer kan du stort set foretage alle de ændringer, du ønsker i din database. Du finder dog ikke disse handlinger i en migrering, der er blevet oprettet automatisk med makemigrations ledelseskommando.

Siden Django 2.0 er der også et par PostgreSQL-specifikke operationer tilgængelige i django.contrib.postgres.operations som du kan bruge til at installere forskellige PostgreSQL-udvidelser:

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

Bemærk, at en migrering, der indeholder en af ​​disse operationer, kræver en databasebruger med superbrugerrettigheder.

Sidst, men ikke mindst, kan du også oprette dine egne operationsklasser. Hvis du vil se nærmere på det, så tag et kig på Django-dokumentationen om oprettelse af tilpassede migreringsoperationer.



Migreringsafhængigheder

dependencies liste i en migreringsklasse indeholder alle migreringer, der skal anvendes, før denne migrering kan anvendes.

I 0001_initial.py migration, du så ovenfor, skal intet anvendes før, så der er ingen afhængigheder. Lad os se på den anden migrering i historical_prices app. I filen 0002_switch_to_decimals.py , dependencies attribut for Migration har en post:

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

Afhængigheden ovenfor siger, at migration 0001_initial af appen historical_data skal køres først. Det giver mening, fordi migreringen 0001_initial opretter tabellen, der indeholder det felt, som migreringen 0002_switch_to_decimals ønsker at ændre sig.

En migrering kan også være afhængig af en migrering fra en anden app, sådan som denne:

class Migration(migrations.Migration):
    ...

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

Dette er normalt nødvendigt, hvis en model har en fremmednøgle, der peger på en model i en anden app.

Alternativt kan du også gennemtvinge, at en migrering køres før endnu en migrering ved hjælp af attributten run_before :

class Migration(migrations.Migration):
    ...

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

Afhængigheder kan også kombineres, så du kan have flere afhængigheder. Denne funktionalitet giver en masse fleksibilitet, da du kan rumme fremmednøgler, der afhænger af modeller fra forskellige apps.

Muligheden for eksplicit at definere afhængigheder mellem migreringer betyder også, at nummereringen af ​​migreringerne (normalt 0001 , 0002 , 0003 , …) repræsenterer ikke strengt den rækkefølge, som migreringerne anvendes i. Du kan tilføje enhver afhængighed, du ønsker, og dermed kontrollere rækkefølgen uden at skulle omnummerere alle migreringerne.



Visning af migreringen

Du behøver generelt ikke bekymre dig om den SQL, som migreringer genererer. Men hvis du vil dobbelttjekke, at den genererede SQL giver mening eller bare er nysgerrig efter, hvordan den ser ud, så dækker Django dig med sqlmigrate ledelseskommando:

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

Hvis du gør det, vises de underliggende SQL-forespørgsler, der vil blive genereret af den angivne migrering, baseret på databasen i din settings.py fil. Når du sender parameteren --backwards , Django genererer SQL'en for at annullere anvendelsen af ​​migreringen:

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

Når du ser outputtet af sqlmigrate for en lidt mere kompleks migrering, vil du måske sætte pris på, at du ikke behøver at lave al denne SQL i hånden!




Hvordan Django registrerer ændringer i dine modeller

Du har set, hvordan en migreringsfil ser ud, og hvordan dens liste over Operation klasser definerer de ændringer, der udføres i databasen. Men hvordan ved Django præcist, hvilke operationer der skal gå ind i en migrationsfil? Du kan forvente, at Django sammenligner dine modeller med dit databaseskema, men det er ikke tilfældet.

Når du kører makemigrations , Django gør ikke inspicere din database. Den sammenligner heller ikke din modelfil med en tidligere version. I stedet gennemgår Django alle migreringer, der er blevet anvendt, og opbygger en projekttilstand over, hvordan modellerne skal se ud. Denne projekttilstand sammenlignes derefter med dine nuværende modeldefinitioner, og der oprettes en liste over operationer, som, når den anvendes, vil bringe projekttilstanden ajour med modeldefinitionerne.


Spil skak med Django

Du kan tænke på dine modeller som et skakbræt, og Django er en skakstormester, der ser dig spille mod dig selv. Men stormesteren overvåger ikke alle dine bevægelser. Stormesteren ser kun på tavlen, når du råber makemigrations .

Fordi der kun er et begrænset sæt mulige træk (og stormesteren er en stormester), kan hun finde på de træk, der er sket, siden hun sidst så på tavlen. Hun tager nogle noter og lader dig spille, indtil du råber makemigrations igen.

Når man ser på tavlen næste gang, husker stormesteren ikke, hvordan skakbrættet så ud sidst, men hun kan gennemgå sine noter fra de tidligere træk og bygge en mental model af, hvordan skakbrættet så ud.

Nu, når du råber migrate , vil stormesteren afspille alle de optagne træk på et andet skakbræt og notere i et regneark, hvilke af hendes rekorder der allerede er blevet anvendt. Dette andet skakbræt er din database, og regnearket er django_migrations tabel.

Denne analogi er ganske passende, fordi den godt illustrerer nogle adfærd ved Django-migreringer:

  • Django-migreringer forsøger at være effektive: Ligesom stormesteren antager, at du lavede det mindste antal træk, vil Django forsøge at skabe de mest effektive migreringer. Hvis du tilføjer et felt med navnet A til en model, og omdøb den derefter til B , og kør derefter makemigrations , så vil Django oprette en ny migrering for at tilføje et felt med navnet B .

  • Django-migreringer har deres begrænsninger: Hvis du laver mange træk, før du lader stormesteren se på skakbrættet, så er hun måske ikke i stand til at spore de nøjagtige bevægelser af hver brik. På samme måde kommer Django muligvis ikke med den korrekte migrering, hvis du foretager for mange ændringer på én gang.

  • Django-migrering forventer, at du spiller efter reglerne: Når du gør noget uventet, som at tage et tilfældigt stykke fra brættet eller rode med noderne, bemærker stormesteren måske ikke i starten, men før eller siden vil hun kaste hænderne op og nægte at fortsætte. Det samme sker, når du roder med django_migrations tabel eller ændre dit databaseskema uden for migreringer, for eksempel ved at slette databasetabellen for en model.



Forstå SeparateDatabaseAndState

Nu hvor du kender til den projektstat, som Django bygger, er det tid til at se nærmere på operationen SeparateDatabaseAndState . Denne operation kan gøre præcis, hvad navnet antyder:den kan adskille projekttilstanden (den mentale model, Django bygger) fra din database.

SeparateDatabaseAndState instansieres med to lister over operationer:

  1. state_operations indeholder operationer, der kun anvendes på projekttilstanden.
  2. database_operations indeholder operationer, der kun anvendes på databasen.

Denne handling lader dig foretage enhver form for ændring af din database, men det er dit ansvar at sørge for, at projekttilstanden passer til databasen bagefter. Eksempler på use cases for SeparateDatabaseAndState flytter en model fra en app til en anden eller opretter et indeks på en enorm database uden nedetid.

SeparateDatabaseAndState er en avanceret operation, og du behøver ikke på din første dag at arbejde med migreringer og måske slet ikke. SeparateDatabaseAndState ligner hjertekirurgi. Det indebærer en del risiko og er ikke noget, du bare gør for sjov, men nogle gange er det en nødvendig procedure for at holde patienten i live.




Konklusion

Dette afslutter dit dybe dyk i Django-migreringer. Tillykke! Du har dækket en hel del avancerede emner og har nu en solid forståelse af, hvad der sker under migrationernes hætte.

Du lærte at:

  • Django holder styr på anvendte migreringer i Django-migreringstabellen.
  • Django-migreringer består af almindelige Python-filer, der indeholder en Migration klasse.
  • Django ved, hvilke ændringer der skal udføres fra operations liste i Migration klasser.
  • Django sammenligner dine modeller med en projekttilstand, den bygger ud fra migreringerne.

Med denne viden er du nu klar til at tackle tredje del af serien om Django-migreringer, hvor du lærer, hvordan du bruger datamigreringer til sikkert at foretage engangsændringer af dine data. Hold dig opdateret!

Denne artikel brugte bitcoin_tracker Django-projekt bygget i Django Migrations:A Primer. Du kan enten genskabe det pågældende projekt ved at gennemgå den artikel, eller du kan downloade kildekoden:

Download kildekode: Klik her for at downloade koden til Django-migreringsprojektet, som du vil bruge i denne artikel.



  1. Sådan finder du standardfilplaceringen for datafiler og logfiler i SQL Server

  2. UnicodeEncodeError:'latin-1' codec kan ikke indkode tegn

  3. Sådan får du information om kompileringsfejl i Oracle/TOAD

  4. Graver dybere ind i Django-migreringer