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

Django Migrations:A Primer

Se nu Denne vejledning har et relateret videokursus oprettet af Real Python-teamet. Se det sammen med det skriftlige selvstudie for at uddybe din forståelse:Django Migrations 101

Siden version 1.7 er Django kommet med indbygget understøttelse af databasemigrering. I Django går databasemigreringer normalt hånd i hånd med modeller:hver gang du koder en ny model, genererer du også en migrering for at skabe den nødvendige tabel i databasen. Migrationer kan dog meget mere.

Du kommer til at lære, hvordan Django Migrations fungerer, og hvordan du kan få mest muligt ud af dem i løbet af fire artikler og en video:

  • Del 1:Django Migrations:A Primer (aktuel artikel)
  • Del 2:Dybere ind i migrationer
  • Del 3:Datamigrering
  • Video:Django 1.7 Migrations - primer

I denne artikel bliver du fortrolig med Django-migreringer og lærer følgende:

  • Sådan opretter du databasetabeller uden at skrive SQL
  • Sådan ændrer du automatisk din database, efter du har ændret dine modeller
  • Sådan gendannes ændringer foretaget i din database

Gratis bonus: Klik her for at få adgang til en gratis Django Learning Resources Guide (PDF), der viser dig tips og tricks samt almindelige faldgruber, du skal undgå, når du bygger Python + Django-webapplikationer.


De problemer, som migrationer løser

Hvis du er ny til Django eller webudvikling generelt, er du måske ikke bekendt med begrebet databasemigrering, og det virker måske ikke indlysende, hvorfor det er en god idé.

Lad os først hurtigt definere et par udtryk for at sikre, at alle er på samme side. Django er designet til at arbejde med en relationsdatabase, gemt i et relationsdatabasestyringssystem som PostgreSQL, MySQL eller SQLite.

I en relationsdatabase er data organiseret i tabeller. En databasetabel har et vist antal kolonner, men den kan have et hvilket som helst antal rækker. Hver kolonne har en bestemt datatype, såsom en streng med en bestemt maksimal længde eller et positivt heltal. Beskrivelsen af ​​alle tabeller med deres kolonner og deres respektive datatyper kaldes et databaseskema.

Alle databasesystemer understøttet af Django bruger sproget SQL til at oprette, læse, opdatere og slette data i en relationsdatabase. SQL bruges også til at oprette, ændre og slette selve databasetabellerne.

At arbejde direkte med SQL kan være ret besværligt, så for at gøre dit liv lettere, kommer Django med en objektrelationel mapper, eller ORM for kort. ORM kortlægger den relationelle database til en verden af ​​objektorienteret programmering. I stedet for at definere databasetabeller i SQL, skriver du Django-modeller i Python. Dine modeller definerer databasefelter, som svarer til kolonnerne i deres databasetabeller.

Her er et eksempel på, hvordan en Django-modelklasse er knyttet til en databasetabel:

Men bare det at definere en modelklasse i en Python-fil får ikke en databasetabel til at dukke op fra ingenting. At oprette databasetabellerne til at gemme dine Django-modeller er opgaven med en databasemigrering. Derudover skal databasen også ændres, når du foretager en ændring af dine modeller, f.eks. ved at tilføje et felt. Migrationer håndterer det også.

Her er et par måder, hvorpå Django-migreringer gør dit liv lettere.


Foretag databaseændringer uden SQL

Uden migreringer ville du skulle oprette forbindelse til din database og indtaste en masse SQL-kommandoer eller bruge et grafisk værktøj som PHPMyAdmin til at ændre databaseskemaet, hver gang du ville ændre din modeldefinition.

I Django er migreringer primært skrevet i Python, så du behøver ikke at kende nogen SQL, medmindre du har virkelig avancerede use cases.



Undgå gentagelse

At oprette en model og derefter skrive SQL for at oprette databasetabellerne til den ville være gentaget.

Migreringer genereres fra dine modeller, og sørg for, at du ikke gentager dig selv.



Sikring af modeldefinitioner og databaseskemaet synkroniseret

Normalt har du flere forekomster af din database, for eksempel en database for hver udvikler i dit team, en database til test og en database med live data.

Uden migreringer bliver du nødt til at udføre skemaændringer på hver enkelt af dine databaser, og du bliver nødt til at holde styr på, hvilke ændringer der allerede er foretaget i hvilken database.

Med Django Migrations kan du nemt holde flere databaser synkroniseret med dine modeller.



Sporing af databaseskemaændring i versionskontrol

Et versionskontrolsystem som Git er fremragende til kode, men ikke så meget til databaseskemaer.

Da migreringer er almindelige Python i Django, kan du placere dem i et versionskontrolsystem ligesom ethvert andet stykke kode.

Nu er du forhåbentlig overbevist om, at migrationer er et nyttigt og kraftfuldt værktøj. Lad os begynde at lære, hvordan man frigør den kraft.




Opsætning af et Django-projekt

Igennem denne tutorial kommer du til at arbejde på en simpel Bitcoin-tracker-app som et eksempelprojekt.

Det første trin er at installere Django. Sådan gør du det på Linux eller macOS X ved hjælp af et virtuelt miljø:

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

Nu har du oprettet et nyt virtuelt miljø og aktiveret det, samt installeret Django i det virtuelle miljø.

Bemærk, at på Windows vil du køre env/bin/activate.bat i stedet for source env/bin/activate for at aktivere dit virtuelle miljø.

For lettere læsbarhed vil konsoleksempler ikke inkludere (env) del af prompten fra nu af.

Med Django installeret kan du oprette projektet ved hjælp af følgende kommandoer:

$ django-admin.py startproject bitcoin_tracker
$ cd bitcoin_tracker
$ python manage.py startapp historical_data

Dette giver dig et simpelt projekt og en app kaldet historical_data . Du skulle nu have denne mappestruktur:

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

Inden for bitcoin_tracker mappe, er der to undermapper:bitcoin_tracker for projektdækkende filer og historical_data indeholdende filer til den app, du har oprettet.

For at oprette en model skal du tilføje denne klasse i 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()

Dette er den grundlæggende model til at holde styr på Bitcoin-priserne.

Glem heller ikke at tilføje den nyoprettede app til settings.INSTALLED_APPS . Åbn bitcoin_tracker/settings.py og tilføj historical_data til listen INSTALLED_APPS , sådan her:

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

De andre indstillinger er fine til dette projekt. Denne vejledning antager, at dit projekt er konfigureret til at bruge en SQLite-database, som er standard.



Oprettelse af migreringer

Med modellen oprettet, er den første ting, du skal gøre, at oprette en migrering til den. Du kan gøre dette med følgende kommando:

$ python manage.py makemigrations historical_data
Migrations for 'historical_data':
  historical_data/migrations/0001_initial.py
    - Create model PriceHistory

Bemærk: Angivelse af navnet på applikationen, historical_data , er valgfrit. Hvis du udelader det, oprettes migreringer for alle apps.

Dette opretter migreringsfilen, der instruerer Django om, hvordan man opretter databasetabellerne for de modeller, der er defineret i din applikation. Lad os se på bibliotekstræet igen:

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

Som du kan se, er migrations biblioteket indeholder nu en ny fil:0001_initial.py .

Bemærk: Du bemærker måske, at du kører makemigrations kommandoen oprettede også filen db.sqlite3 , som indeholder din SQLite-database.

Når du forsøger at få adgang til en ikke-eksisterende SQLite3-databasefil, oprettes den automatisk.

Denne adfærd er unik for SQLite3. Hvis du bruger en anden database-backend som PostgreSQL eller MySQL, skal du selv oprette databasen før kører makemigrations .

Du kan tage et kig på databasen med dbshell ledelseskommando. I SQLite er kommandoen til at liste alle tabeller simpelthen .tables :

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

Databasen er stadig tom. Det ændrer sig, når du anvender migreringen. Skriv .quit for at afslutte SQLite-skallen.



Anvendelse af migreringer

Du har nu oprettet migreringen, men for rent faktisk at foretage ændringer i databasen, skal du anvende den med ledelseskommandoen 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

Der sker meget her! Ifølge outputtet er din migrering blevet anvendt. Men hvor kommer alle de andre migrationer fra?

Husk indstillingen INSTALLED_APPS ? Nogle af de andre apps, der er anført der, kommer også med migreringer og migrate management-kommandoen anvender som standard migreringerne for alle installerede apps.

Kig igen på databasen:

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

Nu er der flere borde. Deres navne giver dig en idé om deres formål. Migreringen, som du genererede i det forrige trin, har skabt historical_data_pricehistory bord. Lad os inspicere det ved hjælp af .schema kommando:

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 kommandoen udskriver CREATE erklæring, som du ville udføre for at oprette tabellen. Parameteren --indent formaterer det fint. Selvom du ikke er bekendt med SQL-syntaks, kan du se, at skemaet for historical_data_pricehistory tabel afspejler felterne i PriceHistory model.

Der er en kolonne for hvert felt og en ekstra kolonne id for den primære nøgle, som Django opretter automatisk, medmindre du udtrykkeligt angiver en primær nøgle i din model.

Her er, hvad der sker, hvis du kører migrate kommando igen:

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

Ikke noget! Django husker, hvilke migreringer der allerede er blevet anvendt og forsøger ikke at køre dem igen.

Det er værd at bemærke, at du også kan begrænse migrate ledelseskommando til en enkelt app:

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

Som du kan se, anvender Django nu kun migreringer for historical_data app.

Når du kører migreringerne for første gang, er det en god idé at anvende alle migreringer for at sikre, at din database indeholder de nødvendige tabeller for de funktioner, du kan tage for givet, såsom brugergodkendelse og sessioner.



Ændring af modeller

Dine modeller er ikke hugget i sten. Dine modeller vil ændre sig, efterhånden som dit Django-projekt får flere funktioner. Du kan tilføje eller fjerne felter eller ændre deres typer og muligheder.

Når du ændrer definitionen af ​​en model, skal databasetabellerne, der bruges til at gemme disse modeller, også ændres. Hvis dine modeldefinitioner ikke matcher dit nuværende databaseskema, vil du højst sandsynligt løbe ind i en django.db.utils.OperationalError .

Så hvordan ændrer du databasetabellerne? Ved at oprette og anvende en migrering.

Mens du tester din Bitcoin-tracker, indser du, at du har lavet en fejl. Folk sælger brøkdele af en Bitcoin, så feltet volume skal være af typen DecimalField i stedet for PositiveIntegerField .

Lad os ændre modellen til at se sådan ud:

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)

Uden migreringer ville du skulle finde ud af SQL-syntaksen for at lave et PositiveIntegerField ind i et DecimalField . Heldigvis klarer Django det for dig. Bare fortæl den at foretage migreringer:

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

Bemærk: Navnet på migreringsfilen (0002_auto_20181112_1950.py ) er baseret på det aktuelle tidspunkt og vil være anderledes, hvis du følger med på dit system.

Nu anvender du denne migrering til din database:

$ 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

Migreringen er blevet anvendt med succes, så du kan bruge dbshell for at bekræfte, at ændringerne havde en effekt:

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

Hvis du sammenligner det nye skema med det skema, du så tidligere, vil du bemærke, at typen af ​​volume kolonne er ændret fra integer til decimal for at afspejle ændringen af ​​volume felt i modellen fra PositiveIntegerField til DecimalField .



Udføring af migreringer

Hvis du vil vide, hvilke migrationer der findes i et Django-projekt, behøver du ikke at grave igennem migrations mapper over dine installerede apps. Du kan bruge showmigrations kommando:

$ ./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

Dette viser alle apps i projektet og de migreringer, der er knyttet til hver app. Det vil også sætte et stort X ud for de migreringer, der allerede er blevet anvendt.

For vores lille eksempel, showmigrations kommandoen er ikke særlig spændende, men den er praktisk, når du begynder at arbejde på en eksisterende kodebase eller arbejder i et team, hvor du ikke er den eneste person, der tilføjer migreringer.



Ikke-anvendelse af migreringer

Nu ved du, hvordan du foretager ændringer i dit databaseskema ved at oprette og anvende migreringer. På et tidspunkt vil du måske fortryde ændringer og skifte tilbage til et tidligere databaseskema, fordi du:

  • Vil du teste en migrering, som en kollega skrev
  • Indse, at en ændring, du har foretaget, var en dårlig idé
  • Arbejd på flere funktioner med forskellige databaseændringer parallelt
  • Ønsker at gendanne en sikkerhedskopi, der blev oprettet, da databasen stadig havde et ældre skema

Heldigvis behøver migrationer ikke at være en ensrettet gade. I mange tilfælde kan virkningerne af en migrering fortrydes ved at annullere en migrering. For at annullere en migrering skal du kalde migrate med navnet på appen og navnet på migreringen før den migrering, du vil annullere.

Hvis du vil gendanne migreringen 0002_auto_20181112_1950 i dine historical_data app, skal du bestå 0001_initial som et argument til migrate kommando:

$ 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

Migreringen er ikke blevet anvendt, hvilket betyder, at ændringerne til databasen er blevet vendt tilbage.

Fjernelse af anvendelsen af ​​en migrering fjerner ikke dens migreringsfil. Næste gang du kører migrate kommando, vil migreringen blive anvendt igen.

Forsigtig: Forveksle ikke uanvendelige migreringer med den fortryd-handling, du er vant til fra din foretrukne teksteditor.

Ikke alle databaseoperationer kan vendes fuldstændigt tilbage. Hvis du fjerner et felt fra en model, opretter en migrering og anvender det, vil Django fjerne den respektive kolonne fra databasen.

Hvis du fjerner denne migrering, genoprettes kolonnen, men det vil ikke bringe de data tilbage, der blev gemt i den kolonne!

Når du har med migreringsnavne at gøre, sparer Django dig for et par tastetryk ved ikke at tvinge dig til at stave hele navnet på migreringen. Det behøver lige nok af navnet til at identificere det entydigt.

I det forrige eksempel ville det have været nok at køre python manage.py migrate historical_data 0001 .



Navngivningsmigreringer

I ovenstående eksempel fandt Django på et navn til migreringen baseret på tidsstemplet - noget i stil med *0002_auto_20181112_1950 . Hvis du ikke er tilfreds med det, kan du bruge --name parameter for at angive et brugerdefineret navn (uden .py udvidelse).

For at prøve det, skal du først fjerne den gamle migrering. Du har allerede fjernet den, så du kan trygt slette filen:

$ rm historical_data/migrations/0002_auto_20181112_1950.py

Nu kan du genskabe det med et mere beskrivende navn:

$ ./manage.py makemigrations historical_data --name switch_to_decimals

Dette vil skabe den samme migrering som før undtagen med det nye navn 0002_switch_to_decimals .



Konklusion

Du dækkede en del af jorden i dette selvstudie og lærte det grundlæggende i Django-migrering.

For at opsummere ser de grundlæggende trin til at bruge Django-migreringer sådan ud:

  1. Opret eller opdater en model
  2. Kør ./manage.py makemigrations <app_name>
  3. Kør ./manage.py migrate for at migrere alt eller ./manage.py migrate <app_name> for at migrere en individuel app
  4. Gentag efter behov

Det er det! Denne arbejdsgang vil fungere det meste af tiden, men hvis tingene ikke fungerer som forventet, ved du også, hvordan du angiver og annullerer migreringer.

Hvis du tidligere har oprettet og ændret dine databasetabeller med håndskrevet SQL, er du nu blevet meget mere effektiv ved at uddelegere dette arbejde til Django-migreringer.

I det næste selvstudie i denne serie vil du grave dybere ned i emnet og lære, hvordan Django Migrations fungerer under hætten.

Gratis bonus: Klik her for at få adgang til en gratis Django Learning Resources Guide (PDF), der viser dig tips og tricks samt almindelige faldgruber, du skal undgå, når du bygger Python + Django-webapplikationer.

Skål!



Video



  1. SQL undtagen

  2. SQL - Sådan vælger du en række med en kolonne med maks. værdi

  3. Oversigt over DBCC CheckDB-funktion

  4. SQL Server-godkendelse vs. Windows-godkendelse:Hvilken skal bruges og hvornår