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

Migrazioni Django:una guida

Guarda ora Questo tutorial ha un corso video correlato creato dal team di Real Python. Guardalo insieme al tutorial scritto per approfondire la tua comprensione:Django Migrations 101

Dalla versione 1.7, Django è dotato del supporto integrato per le migrazioni di database. In Django, le migrazioni del database di solito vanno di pari passo con i modelli:ogni volta che si codifica un nuovo modello, si genera anche una migrazione per creare la tabella necessaria nel database. Tuttavia, le migrazioni possono fare molto di più.

Imparerai come funzionano le migrazioni Django e come trarne il massimo nel corso di quattro articoli e un video:

  • Parte 1:Django Migrations:A Primer (articolo attuale)
  • Parte 2:Approfondire le migrazioni
  • Parte 3:Migrazioni dei dati
  • Video:Migrazioni Django 1.7 - primer

In questo articolo, ti familiarizzerai con le migrazioni di Django e imparerai quanto segue:

  • Come creare tabelle di database senza scrivere SQL
  • Come modificare automaticamente il database dopo aver modificato i modelli
  • Come ripristinare le modifiche apportate al database

Bonus gratuito: Fai clic qui per accedere a una guida gratuita alle risorse di apprendimento di Django (PDF) che mostra suggerimenti e trucchi, nonché le insidie ​​​​comuni da evitare durante la creazione di applicazioni Web Python + Django.


I problemi risolti dalle migrazioni

Se non conosci Django o lo sviluppo web in generale, potresti non avere familiarità con il concetto di migrazioni di database e potrebbe non sembrare ovvio il motivo per cui sono una buona idea.

Innanzitutto, definiamo rapidamente un paio di termini per assicurarci che tutti siano sulla stessa pagina. Django è progettato per funzionare con un database relazionale, archiviato in un sistema di gestione di database relazionali come PostgreSQL, MySQL o SQLite.

In un database relazionale, i dati sono organizzati in tabelle. Una tabella di database ha un certo numero di colonne, ma può avere un numero qualsiasi di righe. Ogni colonna ha un tipo di dati specifico, come una stringa di una certa lunghezza massima o un numero intero positivo. La descrizione di tutte le tabelle con le relative colonne e i rispettivi tipi di dati è denominata schema di database.

Tutti i sistemi di database supportati da Django utilizzano il linguaggio SQL per creare, leggere, aggiornare ed eliminare i dati in un database relazionale. SQL viene utilizzato anche per creare, modificare ed eliminare le stesse tabelle del database.

Lavorare direttamente con SQL può essere piuttosto ingombrante, quindi per semplificarti la vita, Django viene fornito con un mappatore relazionale a oggetti, o ORM in breve. L'ORM associa il database relazionale al mondo della programmazione orientata agli oggetti. Invece di definire tabelle di database in SQL, scrivi modelli Django in Python. I tuoi modelli definiscono i campi del database, che corrispondono alle colonne nelle loro tabelle del database.

Ecco un esempio di come una classe modello Django viene mappata su una tabella di database:

Ma la semplice definizione di una classe modello in un file Python non fa apparire magicamente una tabella di database dal nulla. La creazione delle tabelle del database per archiviare i tuoi modelli Django è il lavoro di una migrazione del database. Inoltre, ogni volta che apporti una modifica ai tuoi modelli, ad esempio aggiungendo un campo, anche il database deve essere modificato. Le migrazioni gestiscono anche questo.

Ecco alcuni modi in cui le migrazioni di Django ti semplificano la vita.


Apportare modifiche al database senza SQL

Senza migrazioni, dovresti connetterti al tuo database e digitare una serie di comandi SQL o utilizzare uno strumento grafico come PHPMyAdmin per modificare lo schema del database ogni volta che desideri modificare la definizione del modello.

In Django, le migrazioni sono scritte principalmente in Python, quindi non devi conoscere alcun SQL a meno che tu non abbia casi d'uso davvero avanzati.



Evitare la ripetizione

Creare un modello e quindi scrivere SQL per creare le tabelle del database per esso sarebbe ripetitivo.

Le migrazioni vengono generate dai tuoi modelli, assicurandoti di non ripeterti.



Garantire la sincronizzazione delle definizioni del modello e dello schema del database

Di solito, hai più istanze del tuo database, ad esempio un database per ogni sviluppatore del tuo team, un database per i test e un database con dati in tempo reale.

Senza migrazioni, dovrai apportare modifiche allo schema su ciascuno dei tuoi database e dovrai tenere traccia di quali modifiche sono già state apportate a quale database.

Con Django Migrations, puoi facilmente sincronizzare più database con i tuoi modelli.



Tracciamento delle modifiche allo schema del database nel controllo della versione

Un sistema di controllo della versione, come Git, è eccellente per il codice, ma non tanto per gli schemi di database.

Poiché le migrazioni sono semplici Python in Django, puoi inserirle in un sistema di controllo della versione proprio come qualsiasi altro pezzo di codice.

A questo punto, si spera che tu sia convinto che le migrazioni siano uno strumento utile e potente. Iniziamo a imparare come liberare quel potere.




Impostazione di un progetto Django

Durante questo tutorial, lavorerai su una semplice app di monitoraggio Bitcoin come progetto di esempio.

Il primo passo è installare Django. Ecco come farlo su Linux o macOS X utilizzando un ambiente virtuale:

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

Ora hai creato un nuovo ambiente virtuale e l'hai attivato, oltre a aver installato Django in quell'ambiente virtuale.

Nota che su Windows dovresti eseguire env/bin/activate.bat invece di source env/bin/activate per attivare il tuo ambiente virtuale.

Per una maggiore leggibilità, gli esempi della console non includeranno il (env) parte del prompt d'ora in poi.

Con Django installato, puoi creare il progetto utilizzando i seguenti comandi:

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

Questo ti dà un semplice progetto e un'app chiamata historical_data . Ora dovresti avere questa struttura di directory:

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

All'interno del bitcoin_tracker directory, ci sono due sottodirectory:bitcoin_tracker per file a livello di progetto e historical_data contenente i file per l'app che hai creato.

Ora, per creare un modello, aggiungi questa classe 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()

Questo è il modello base per tenere traccia dei prezzi di Bitcoin.

Inoltre, non dimenticare di aggiungere l'app appena creata a settings.INSTALLED_APPS . Apri bitcoin_tracker/settings.py e aggiungi historical_data alla lista INSTALLED_APPS , in questo modo:

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

Le altre impostazioni vanno bene per questo progetto. Questo tutorial presuppone che il tuo progetto sia configurato per utilizzare un database SQLite, che è l'impostazione predefinita.



Creazione di migrazioni

Con il modello creato, la prima cosa che devi fare è creare una migrazione per esso. Puoi farlo con il seguente comando:

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

Nota: Specificando il nome dell'applicazione, historical_data , è facoltativo. Lasciarlo disattivato crea migrazioni per tutte le app.

Questo crea il file di migrazione che istruisce Django su come creare le tabelle di database per i modelli definiti nella tua applicazione. Diamo un'altra occhiata all'albero delle directory:

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

Come puoi vedere, le migrations la directory ora contiene un nuovo file:0001_initial.py .

Nota: Potresti notare che eseguendo makemigrations comando ha anche creato il file db.sqlite3 , che contiene il tuo database SQLite.

Quando si tenta di accedere a un file di database SQLite3 non esistente, verrà creato automaticamente.

Questo comportamento è univoco per SQLite3. Se utilizzi qualsiasi altro database back-end come PostgreSQL o MySQL, devi creare tu stesso il database prima eseguendo makemigrations .

Puoi dare un'occhiata al database con dbshell comando di gestione. In SQLite, il comando per elencare tutte le tabelle è semplicemente .tables :

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

Il database è ancora vuoto. Ciò cambierà quando applichi la migrazione. Digita .quit per uscire dalla shell SQLite.



Applicazione delle migrazioni

Ora hai creato la migrazione, ma per apportare effettivamente delle modifiche al database, devi applicarla con il comando di gestione 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

C'è molto da fare qui! In base all'output, la migrazione è stata applicata correttamente. Ma da dove provengono tutte le altre migrazioni?

Ricorda l'impostazione INSTALLED_APPS ? Anche alcune delle altre app elencate sono dotate di migrazioni e migrate il comando di gestione applica le migrazioni per tutte le app installate per impostazione predefinita.

Dai un'altra occhiata al database:

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

Ora ci sono più tabelle. I loro nomi ti danno un'idea del loro scopo. La migrazione che hai generato nel passaggio precedente ha creato historical_data_pricehistory tavolo. Esaminiamolo usando il .schema comando:

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

Il .schema il comando stampa il CREATE istruzione che eseguiresti per creare la tabella. Il parametro --indent lo formatta bene. Anche se non hai familiarità con la sintassi SQL, puoi vedere che lo schema di historical_data_pricehistory la tabella riflette i campi della PriceHistory modello.

C'è una colonna per ogni campo e una colonna aggiuntiva id per la chiave primaria, che Django crea automaticamente a meno che tu non specifichi esplicitamente una chiave primaria nel tuo modello.

Ecco cosa succede se esegui migrate comando di nuovo:

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

Niente! Django ricorda quali migrazioni sono già state applicate e non tenta di eseguirle nuovamente.

Vale la pena notare che puoi anche limitare la migrate comando di gestione in una singola app:

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

Come puoi vedere, Django ora applica le migrazioni solo per i historical_data app.

Quando esegui le migrazioni per la prima volta, è una buona idea applicare tutte le migrazioni per assicurarti che il tuo database contenga le tabelle necessarie per le funzionalità che potresti dare per scontate, come l'autenticazione utente e le sessioni.



Cambiare i modelli

I tuoi modelli non sono scolpiti nella pietra. I tuoi modelli cambieranno man mano che il tuo progetto Django acquisirà più funzionalità. Potresti aggiungere o rimuovere campi o cambiarne i tipi e le opzioni.

Quando si modifica la definizione di un modello, è necessario modificare anche le tabelle del database utilizzate per archiviare questi modelli. Se le definizioni del tuo modello non corrispondono allo schema del database corrente, molto probabilmente ti imbatterai in un django.db.utils.OperationalError .

Quindi, come si cambiano le tabelle del database? Creando e applicando una migrazione.

Durante il test del tuo tracker Bitcoin, ti rendi conto di aver commesso un errore. Le persone vendono frazioni di Bitcoin, quindi il campo volume dovrebbe essere del tipo DecimalField invece di PositiveIntegerField .

Cambiamo il modello in modo che assomigli a questo:

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)

Senza migrazioni, dovresti capire la sintassi SQL per trasformare un PositiveIntegerField in un DecimalField . Fortunatamente, Django se ne occuperà per te. Basta dirgli di effettuare le migrazioni:

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

Nota: Il nome del file di migrazione (0002_auto_20181112_1950.py ) è basato sull'ora corrente e sarà diverso se segui il tuo sistema.

Ora applichi questa migrazione al tuo 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

La migrazione è stata applicata correttamente, quindi puoi utilizzare dbshell per verificare che le modifiche abbiano avuto effetto:

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

Se confronti il ​​nuovo schema con lo schema che hai visto in precedenza, noterai che il tipo del volume la colonna è cambiata da integer in decimal per riflettere la modifica del volume campo nel modello da PositiveIntegerField a DecimalField .



Elencare le migrazioni

Se vuoi sapere quali migrazioni esistono in un progetto Django, non devi scavare nelle migrations directory delle app installate. Puoi utilizzare showmigrations comando:

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

Questo elenca tutte le app nel progetto e le migrazioni associate a ciascuna app. Inoltre, metterà una grande X accanto alle migrazioni già applicate.

Per il nostro piccolo esempio, showmigrations command non è particolarmente entusiasmante, ma è utile quando inizi a lavorare su una base di codice esistente o lavori in un team in cui non sei l'unico ad aggiungere migrazioni.



Annulla applicazione delle migrazioni

Ora sai come apportare modifiche allo schema del database creando e applicando migrazioni. Ad un certo punto, potresti voler annullare le modifiche e tornare a uno schema di database precedente perché tu:

  • Vuoi testare una migrazione scritta da un collega
  • Renditi conto che una modifica che hai apportato è stata una cattiva idea
  • Lavora su più funzioni con diverse modifiche al database in parallelo
  • Vuoi ripristinare un backup che è stato creato quando il database aveva ancora uno schema precedente

Fortunatamente, le migrazioni non devono essere una strada a senso unico. In molti casi, gli effetti di una migrazione possono essere annullati annullando l'applicazione di una migrazione. Per annullare l'applicazione di una migrazione, devi chiamare migrate con il nome dell'app e il nome della migrazione prima la migrazione che desideri annullare.

Se desideri annullare la migrazione 0002_auto_20181112_1950 nei tuoi historical_data app, devi passare 0001_initial come argomento per migrate comando:

$ 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

La migrazione non è stata applicata, il che significa che le modifiche al database sono state annullate.

L'annullamento dell'applicazione di una migrazione non rimuove il relativo file di migrazione. La prossima volta che esegui migrate comando, la migrazione verrà applicata nuovamente.

Attenzione: Non confondere l'annullamento dell'applicazione delle migrazioni con l'operazione di annullamento a cui sei abituato dal tuo editor di testo preferito.

Non tutte le operazioni del database possono essere completamente ripristinate. Se rimuovi un campo da un modello, crei una migrazione e la applichi, Django rimuoverà la rispettiva colonna dal database.

L'annullamento dell'applicazione di tale migrazione ricreerà la colonna, ma non riporterà i dati che erano stati archiviati in quella colonna!

Quando hai a che fare con i nomi della migrazione, Django ti risparmia alcune sequenze di tasti non costringendoti a scrivere l'intero nome della migrazione. Ha bisogno solo di un nome sufficiente per identificarlo in modo univoco.

Nell'esempio precedente sarebbe bastato eseguire python manage.py migrate historical_data 0001 .



Migrazioni di denominazione

Nell'esempio sopra, Django ha inventato un nome per la migrazione basato sul timestamp, qualcosa come *0002_auto_20181112_1950 . Se non sei soddisfatto, puoi usare il --name parametro per fornire un nome personalizzato (senza .py estensione).

Per provarlo, devi prima rimuovere la vecchia migrazione. L'hai già disapplicato, quindi puoi eliminare il file in sicurezza:

$ rm historical_data/migrations/0002_auto_20181112_1950.py

Ora puoi ricrearlo con un nome più descrittivo:

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

Questo creerà la stessa migrazione di prima tranne che con il nuovo nome di 0002_switch_to_decimals .



Conclusione

Hai coperto un bel po' di terreno in questo tutorial e hai appreso i fondamenti delle migrazioni di Django.

Per ricapitolare, i passaggi di base per utilizzare le migrazioni di Django sono questi:

  1. Crea o aggiorna un modello
  2. Esegui ./manage.py makemigrations <app_name>
  3. Esegui ./manage.py migrate per migrare tutto o ./manage.py migrate <app_name> per migrare una singola app
  4. Ripetere se necessario

Questo è tutto! Questo flusso di lavoro funzionerà per la maggior parte del tempo, ma se le cose non vanno come previsto, sai anche come elencare e annullare l'applicazione delle migrazioni.

Se in precedenza hai creato e modificato le tabelle del database con SQL scritto a mano, ora sei diventato molto più efficiente delegando questo lavoro alle migrazioni di Django.

Nel prossimo tutorial di questa serie, approfondirai l'argomento e imparerai come funzionano le migrazioni Django sotto il cofano.

Bonus gratuito: Fai clic qui per accedere a una guida gratuita alle risorse di apprendimento di Django (PDF) che mostra suggerimenti e trucchi, nonché le insidie ​​​​comuni da evitare durante la creazione di applicazioni Web Python + Django.

Saluti!



Video