sql >> Database >  >> RDS >> Database

Django-migraties:een primeur

Nu kijken Deze tutorial heeft een gerelateerde videocursus gemaakt door het Real Python-team. Bekijk het samen met de geschreven tutorial om je begrip te verdiepen:Django Migrations 101

Sinds versie 1.7 heeft Django ingebouwde ondersteuning voor databasemigraties. In Django gaan databasemigraties meestal hand in hand met modellen:wanneer u een nieuw model codeert, genereert u ook een migratie om de benodigde tabel in de database te maken. Migraties kunnen echter veel meer.

In vier artikelen en één video leer je hoe Django Migrations werkt en hoe je er het maximale uit kunt halen:

  • Deel 1:Django Migrations:A Primer (huidig ​​artikel)
  • Deel 2:Dieper graven in migraties
  • Deel 3:Gegevensmigraties
  • Video:Django 1.7-migraties - primer

In dit artikel maakt u kennis met Django-migraties en leert u het volgende:

  • Hoe databasetabellen te maken zonder SQL te schrijven
  • Hoe u uw database automatisch kunt wijzigen nadat u uw modellen heeft gewijzigd
  • Wijzigingen in uw database terugdraaien

Gratis bonus: Klik hier om toegang te krijgen tot een gratis Django Learning Resources Guide (PDF) met tips en trucs en veelvoorkomende valkuilen die je moet vermijden bij het bouwen van Python + Django-webapplicaties.


De problemen die migraties oplossen

Als Django of webontwikkeling in het algemeen nieuw voor je is, ben je misschien niet bekend met het concept van databasemigraties, en het lijkt misschien niet duidelijk waarom ze een goed idee zijn.

Laten we eerst snel een aantal termen definiëren om ervoor te zorgen dat iedereen op dezelfde lijn zit. Django is ontworpen om te werken met een relationele database, opgeslagen in een relationeel databasebeheersysteem zoals PostgreSQL, MySQL of SQLite.

In een relationele database zijn gegevens georganiseerd in tabellen. Een databasetabel heeft een bepaald aantal kolommen, maar kan een willekeurig aantal rijen hebben. Elke kolom heeft een specifiek datatype, zoals een string met een bepaalde maximale lengte of een positief geheel getal. De beschrijving van alle tabellen met hun kolommen en hun respectievelijke datatypes wordt een databaseschema genoemd.

Alle databasesystemen die door Django worden ondersteund, gebruiken de taal SQL om gegevens in een relationele database aan te maken, te lezen, bij te werken en te verwijderen. SQL wordt ook gebruikt om de databasetabellen zelf aan te maken, te wijzigen en te verwijderen.

Rechtstreeks werken met SQL kan behoorlijk omslachtig zijn, dus om uw leven gemakkelijker te maken, wordt Django geleverd met een object-relationele mapper, of kortweg ORM. De ORM brengt de relationele database in kaart met de wereld van objectgeoriënteerd programmeren. In plaats van databasetabellen in SQL te definiëren, schrijf je Django-modellen in Python. Uw modellen definiëren databasevelden, die overeenkomen met de kolommen in hun databasetabellen.

Hier is een voorbeeld van hoe een Django-modelklasse wordt toegewezen aan een databasetabel:

Maar alleen het definiëren van een modelklasse in een Python-bestand zorgt er niet voor dat een databasetabel op magische wijze uit het niets verschijnt. Het maken van de databasetabellen om uw Django-modellen op te slaan, is de taak van een databasemigratie. Bovendien, wanneer u een wijziging aanbrengt in uw modellen, zoals het toevoegen van een veld, moet de database ook worden gewijzigd. Migraties regelen dat ook.

Hier zijn een paar manieren waarop Django-migraties uw leven gemakkelijker maken.


Database wijzigen zonder SQL

Zonder migraties zou u verbinding moeten maken met uw database en een aantal SQL-commando's moeten typen of een grafische tool zoals PHPMyAdmin moeten gebruiken om het databaseschema aan te passen telkens als u uw modeldefinitie wilt wijzigen.

In Django worden migraties voornamelijk geschreven in Python, dus je hoeft geen SQL te kennen, tenzij je echt geavanceerde use-cases hebt.



Herhaling vermijden

Een model maken en vervolgens SQL schrijven om de databasetabellen ervoor te maken zou repetitief zijn.

Migraties worden gegenereerd op basis van uw modellen, zodat u zich niet hoeft te herhalen.



Zorgen voor synchronisatie van modeldefinities en het databaseschema

Meestal heb je meerdere instanties van je database, bijvoorbeeld één database voor elke ontwikkelaar in je team, een database om te testen en een database met live data.

Zonder migraties moet u schemawijzigingen uitvoeren op elk van uw databases en moet u bijhouden welke wijzigingen al zijn aangebracht in welke database.

Met Django Migrations kunt u eenvoudig meerdere databases synchroon houden met uw modellen.



Het bijhouden van databaseschemawijzigingen in versiebeheer

Een versiebeheersysteem, zoals Git, is uitstekend geschikt voor code, maar niet zozeer voor databaseschema's.

Omdat migraties in Django gewoon Python zijn, kun je ze in een versiebeheersysteem plaatsen, net als elk ander stukje code.

Inmiddels bent u er hopelijk van overtuigd dat migraties een handig en krachtig hulpmiddel zijn. Laten we beginnen te leren hoe we die kracht kunnen ontketenen.




Een Django-project opzetten

In deze tutorial ga je werken aan een eenvoudige Bitcoin-tracker-app als voorbeeldproject.

De eerste stap is om Django te installeren. Hier is hoe je dat doet op Linux of macOS X met een virtuele omgeving:

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

Nu heb je een nieuwe virtuele omgeving gemaakt en geactiveerd, en ook Django in die virtuele omgeving geïnstalleerd.

Merk op dat u in Windows env/bin/activate.bat . zou uitvoeren in plaats van source env/bin/activate om uw virtuele omgeving te activeren.

Voor een betere leesbaarheid bevatten consolevoorbeelden niet de (env) vanaf nu onderdeel van de prompt.

Als Django is geïnstalleerd, kunt u het project maken met de volgende opdrachten:

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

Dit geeft je een eenvoudig project en een app genaamd historical_data . U zou nu deze mapstructuur moeten hebben:

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

Binnen de bitcoin_tracker directory, zijn er twee subdirectories:bitcoin_tracker voor projectbrede bestanden en historical_data met bestanden voor de app die je hebt gemaakt.

Om nu een model te maken, voegt u deze klasse toe in 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()

Dit is het basismodel om Bitcoin-prijzen bij te houden.

Vergeet ook niet de nieuw gemaakte app toe te voegen aan settings.INSTALLED_APPS . Open bitcoin_tracker/settings.py en voeg historical_data toe naar de lijst INSTALLED_APPS , zoals dit:

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

De andere instellingen zijn prima voor dit project. Deze tutorial gaat ervan uit dat uw project is geconfigureerd om een ​​SQLite-database te gebruiken, wat de standaard is.



Migraties maken

Nadat het model is gemaakt, moet u er eerst een migratie voor maken. U kunt dit doen met het volgende commando:

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

Opmerking: Specificeren van de naam van de applicatie, historical_data , is optioneel. Als u dit uit laat, worden er migraties voor alle apps gemaakt.

Hiermee wordt het migratiebestand gemaakt dat Django instrueert hoe de databasetabellen moeten worden gemaakt voor de modellen die in uw toepassing zijn gedefinieerd. Laten we nog eens naar de mappenboom kijken:

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

Zoals je kunt zien, zijn de migrations directory bevat nu een nieuw bestand:0001_initial.py .

Opmerking: Het is je misschien opgevallen dat het uitvoeren van de makemigrations opdracht maakte ook het bestand db.sqlite3 , die uw SQLite-database bevat.

Wanneer u probeert toegang te krijgen tot een niet-bestaand SQLite3-databasebestand, wordt het automatisch aangemaakt.

Dit gedrag is uniek voor SQLite3. Als u een andere database-backend zoals PostgreSQL of MySQL gebruikt, moet u de database vooraf zelf maken uitvoeren van makemigrations .

U kunt een kijkje nemen in de database met de dbshell beheer commando. In SQLite is de opdracht om alle tabellen weer te geven eenvoudig .tables :

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

De database is nog leeg. Dat verandert als u de migratie toepast. Typ .quit om de SQLite-shell te verlaten.



Migraties toepassen

U hebt nu de migratie gemaakt, maar om daadwerkelijk wijzigingen in de database aan te brengen, moet u deze toepassen met het beheercommando 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

Er gebeurt hier veel! Volgens de output is uw migratie succesvol toegepast. Maar waar komen alle andere migraties vandaan?

Onthoud de instelling INSTALLED_APPS ? Sommige van de andere apps die daar worden vermeld, worden ook geleverd met migraties, en de migrate management commando past standaard de migraties toe voor alle geïnstalleerde apps.

Bekijk de database nog eens:

$ 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 zijn er meerdere tabellen. Hun namen geven je een idee van hun doel. De migratie die u in de vorige stap heeft gegenereerd, heeft de historical_data_pricehistory . gemaakt tafel. Laten we het inspecteren met behulp van het .schema commando:

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

Het .schema commando drukt de CREATE . af instructie die u zou uitvoeren om de tabel te maken. De parameter --indent formatteert het mooi. Zelfs als u niet bekend bent met de SQL-syntaxis, kunt u zien dat het schema van de historical_data_pricehistory tabel weerspiegelt de velden van de PriceHistory model.

Er is een kolom voor elk veld en een extra kolom id voor de primaire sleutel, die Django automatisch maakt, tenzij u expliciet een primaire sleutel opgeeft in uw model.

Dit is wat er gebeurt als je de migrate uitvoert commando opnieuw:

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

Niets! Django onthoudt welke migraties al zijn toegepast en probeert ze niet opnieuw uit te voeren.

Het is vermeldenswaard dat u ook de migrate . kunt beperken beheeropdracht naar een enkele app:

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

Zoals je kunt zien, past Django nu alleen migraties toe voor de historical_data app.

Wanneer u de migraties voor de eerste keer uitvoert, is het een goed idee om alle migraties toe te passen om ervoor te zorgen dat uw database de nodige tabellen bevat voor de functies die u als vanzelfsprekend beschouwt, zoals gebruikersauthenticatie en sessies.



Modellen wijzigen

Uw modellen zijn niet in steen gebeiteld. Je modellen zullen veranderen naarmate je Django-project meer functies krijgt. U kunt velden toevoegen of verwijderen of hun typen en opties wijzigen.

Wanneer u de definitie van een model wijzigt, moeten ook de databasetabellen die worden gebruikt om deze modellen op te slaan, worden gewijzigd. Als uw modeldefinities niet overeenkomen met uw huidige databaseschema, zult u hoogstwaarschijnlijk een django.db.utils.OperationalError tegenkomen .

Dus hoe verander je de databasetabellen? Door een migratie aan te maken en toe te passen.

Tijdens het testen van je Bitcoin-tracker realiseer je je dat je een fout hebt gemaakt. Mensen verkopen fracties van een Bitcoin, dus het veld volume moet van het type DecimalField . zijn in plaats van PositiveIntegerField .

Laten we het model veranderen zodat het er als volgt uitziet:

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)

Zonder migraties zou u de SQL-syntaxis moeten achterhalen om een ​​PositiveIntegerField te maken in een DecimalField . Gelukkig regelt Django dat voor je. Zeg het gewoon om migraties uit te voeren:

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

Opmerking: De naam van het migratiebestand (0002_auto_20181112_1950.py ) is gebaseerd op de huidige tijd en zal anders zijn als u meevolgt op uw systeem.

Nu pas je deze migratie toe op je 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

De migratie is succesvol toegepast, dus u kunt dbshell . gebruiken om te controleren of de wijzigingen effect hebben gehad:

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

Als je het nieuwe schema vergelijkt met het schema dat je eerder zag, zul je merken dat het type van het volume kolom is gewijzigd van integer naar decimal om de verandering van het volume weer te geven veld in het model van PositiveIntegerField naar DecimalField .



Migraties weergeven

Als je wilt weten welke migraties er zijn in een Django-project, hoef je niet door de migrations te graven mappen van uw geïnstalleerde apps. U kunt de showmigrations . gebruiken commando:

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

Dit geeft een overzicht van alle apps in het project en de migraties die aan elke app zijn gekoppeld. Het zal ook een grote X . plaatsen naast de migraties die al zijn toegepast.

Voor ons kleine voorbeeld, de showmigrations commando is niet bijzonder spannend, maar het is handig als je aan een bestaande codebase gaat werken of in een team werkt waar je niet de enige bent die migraties toevoegt.



Migraties ongedaan maken

Nu weet u hoe u wijzigingen aanbrengt in uw databaseschema door migraties te maken en toe te passen. Op een gegeven moment wilt u misschien wijzigingen ongedaan maken en terugschakelen naar een eerder databaseschema omdat u:

  • Wil je een migratie testen die een collega heeft geschreven
  • Besef dat een wijziging die je hebt aangebracht een slecht idee was
  • Werk aan meerdere functies met verschillende databasewijzigingen tegelijk
  • Wilt u een back-up herstellen die is gemaakt toen de database nog een ouder schema had

Gelukkig hoeven migraties geen eenrichtingsverkeer te zijn. In veel gevallen kunnen de effecten van een migratie ongedaan worden gemaakt door een migratie ongedaan te maken. Om een ​​migratie ongedaan te maken, moet u migrate . bellen met de naam van de app en de naam van de migratie voor de migratie die u ongedaan wilt maken.

Als u de migratie 0002_auto_20181112_1950 . wilt terugdraaien in uw historical_data app, moet je door 0001_initial als argument voor de migrate commando:

$ 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

De migratie is ongedaan gemaakt, wat betekent dat de wijzigingen in de database zijn teruggedraaid.

Als u het toepassen van een migratie ongedaan maakt, wordt het migratiebestand niet verwijderd. De volgende keer dat u migrate . uitvoert commando, wordt de migratie opnieuw toegepast.

Let op: Verwar niet-toepasbare migraties niet met de bewerking ongedaan maken die u gewend bent van uw favoriete teksteditor.

Niet alle databasebewerkingen kunnen volledig worden teruggedraaid. Als u een veld uit een model verwijdert, een migratie aanmaakt en toepast, zal Django de betreffende kolom uit de database verwijderen.

Als u die migratie ongedaan maakt, wordt de kolom opnieuw gemaakt, maar worden de gegevens die in die kolom waren opgeslagen niet teruggehaald!

Als je te maken hebt met migratienamen, bespaart Django je een paar toetsaanslagen door je niet te dwingen de hele naam van de migratie te spellen. Het heeft net genoeg van de naam nodig om het uniek te identificeren.

In het vorige voorbeeld zou het voldoende zijn geweest om python manage.py migrate historical_data 0001 uit te voeren. .



Migraties een naam geven

In het bovenstaande voorbeeld bedacht Django een naam voor de migratie op basis van de tijdstempel, zoiets als *0002_auto_20181112_1950 . Als je daar niet tevreden mee bent, kun je de --name . gebruiken parameter om een ​​aangepaste naam op te geven (zonder de .py extensie).

Om dat uit te proberen, moet je eerst de oude migratie verwijderen. Je hebt het al ongedaan gemaakt, dus je kunt het bestand veilig verwijderen:

$ rm historical_data/migrations/0002_auto_20181112_1950.py

Nu kunt u het opnieuw maken met een meer beschrijvende naam:

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

Hiermee wordt dezelfde migratie gemaakt als voorheen, behalve met de nieuwe naam 0002_switch_to_decimals .



Conclusie

Je hebt in deze tutorial heel wat terrein behandeld en de basisprincipes van Django-migraties geleerd.

Samenvattend zien de basisstappen om Django-migraties te gebruiken er als volgt uit:

  1. Een model maken of bijwerken
  2. Voer ./manage.py makemigrations <app_name> uit
  3. Voer ./manage.py migrate om alles te migreren of ./manage.py migrate <app_name> . te migreren een individuele app migreren
  4. Herhaal indien nodig

Dat is het! Deze workflow zal het grootste deel van de tijd werken, maar als dingen niet werken zoals verwacht, weet je ook hoe je migraties kunt opsommen en ongedaan maken.

Als u eerder uw databasetabellen hebt gemaakt en gewijzigd met handgeschreven SQL, bent u nu veel efficiënter geworden door dit werk te delegeren aan Django-migraties.

In de volgende tutorial in deze serie ga je dieper op het onderwerp in en leer je hoe Django-migraties onder de motorkap werken.

Gratis bonus: Klik hier om toegang te krijgen tot een gratis Django Learning Resources Guide (PDF) met tips en trucs en veelvoorkomende valkuilen die je moet vermijden bij het bouwen van Python + Django-webapplicaties.

Proost!



Video



  1. Hoe lijst doorgeven van Java naar Oracle Procedure?

  2. MySQL:kan geen tabel maken (errno:150)

  3. Bepalen of de veldgegevens in Oracle van het type Getal zijn

  4. Hoe een enkele MySQL-tabel te herstellen met mysqldump?