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

Come creare un indice in Django senza tempi di inattività

La gestione delle migrazioni di database è una grande sfida in qualsiasi progetto software. Fortunatamente, a partire dalla versione 1.7, Django viene fornito con un framework di migrazione integrato. Il framework è molto potente e utile nella gestione del cambiamento nei database. Ma la flessibilità fornita dal framework ha richiesto alcuni compromessi. Per comprendere i limiti delle migrazioni di Django, affronterai un problema ben noto:creare un indice in Django senza tempi di inattività.

In questo tutorial imparerai:

  • Come e quando Django genera nuove migrazioni
  • Come controllare i comandi che Django genera per eseguire le migrazioni
  • Come modificare in sicurezza le migrazioni in base alle tue esigenze

Questo tutorial di livello intermedio è progettato per i lettori che hanno già familiarità con le migrazioni di Django. Per un'introduzione a questo argomento, dai un'occhiata a Django Migrations:A Primer.

Bonus gratuito: Fai clic qui per ottenere l'accesso gratuito a ulteriori tutorial e risorse di Django che puoi utilizzare per approfondire le tue capacità di sviluppo Web Python.


Il problema con la creazione di un indice nelle migrazioni Django

Una modifica comune che di solito diventa necessaria quando i dati archiviati dall'applicazione aumentano è l'aggiunta di un indice. Gli indici vengono utilizzati per velocizzare le query e rendere la tua app veloce e reattiva.

Nella maggior parte dei database, l'aggiunta di un indice richiede un blocco esclusivo sulla tabella. Un blocco esclusivo impedisce le operazioni di modifica dei dati (DML) come UPDATE , INSERT e DELETE , durante la creazione dell'indice.

I blocchi vengono ottenuti implicitamente dal database durante l'esecuzione di determinate operazioni. Ad esempio, quando un utente accede alla tua app, Django aggiornerà last_login campo nel auth_user tavolo. Per eseguire l'aggiornamento, il database dovrà prima ottenere un lock sulla riga. Se la riga è attualmente bloccata da un'altra connessione, potresti ricevere un'eccezione del database.

Il blocco di una tabella potrebbe rappresentare un problema quando è necessario mantenere il sistema disponibile durante le migrazioni. Più grande è la tabella, più tempo può essere necessario per creare l'indice. Più tempo è necessario per creare l'indice, più tempo il sistema non è disponibile o non risponde agli utenti.

Alcuni fornitori di database forniscono un modo per creare un indice senza bloccare la tabella. Ad esempio, per creare un indice in PostgreSQL senza bloccare una tabella, puoi usare CONCURRENTLY parola chiave:

CREATE INDEX CONCURRENTLY ix ON table (column);

In Oracle esiste un ONLINE opzione per consentire operazioni DML sulla tabella durante la creazione dell'indice:

CREATE INDEX ix ON table (column) ONLINE;

Durante la generazione delle migrazioni, Django non utilizzerà queste parole chiave speciali. L'esecuzione della migrazione così com'è farà acquisire al database un blocco esclusivo sulla tabella e impedire le operazioni DML durante la creazione dell'indice.

La creazione di un indice contemporaneamente presenta alcuni avvertimenti. È importante comprendere in anticipo i problemi specifici del back-end del database. Ad esempio, un avvertimento in PostgreSQL è che la creazione simultanea di un indice richiede più tempo perché richiede un'ulteriore scansione della tabella.

In questo tutorial utilizzerai le migrazioni di Django per creare un indice su una tabella di grandi dimensioni, senza causare tempi di inattività.

Nota: Per seguire questo tutorial, ti consigliamo di utilizzare un backend PostgreSQL, Django 2.x e Python 3.

È possibile seguire anche altri backend di database. Nei luoghi in cui vengono utilizzate funzionalità SQL uniche per PostgreSQL, modifica l'SQL in modo che corrisponda al back-end del tuo database.



Configurazione

Utilizzerai una Sale inventata modello in un'app chiamata app . In una situazione di vita reale, modelli come Sale sono le tabelle principali nel database e di solito sono molto grandi e memorizzano molti dati:

# models.py

from django.db import models

class Sale(models.Model):
    sold_at = models.DateTimeField(
        auto_now_add=True,
    )
    charged_amount = models.PositiveIntegerField()

Per creare la tabella, genera la migrazione iniziale e applicala:

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

$ python manage migrate
Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0001_initial... OK

Dopo un po', il tavolo delle vendite diventa molto grande e gli utenti iniziano a lamentarsi della lentezza. Durante il monitoraggio del database, hai notato che molte query utilizzano sold_at colonna. Per velocizzare le cose, decidi di aver bisogno di un indice sulla colonna.

Per aggiungere un indice su sold_at , apporti la seguente modifica al modello:

# models.py

from django.db import models

class Sale(models.Model):
    sold_at = models.DateTimeField(
        auto_now_add=True,
        db_index=True,
    )
    charged_amount = models.PositiveIntegerField()

Se esegui questa migrazione così com'è, Django creerà l'indice sulla tabella e sarà bloccato fino al completamento dell'indice. La creazione di un indice su una tabella molto grande può richiedere del tempo e si desidera evitare tempi di inattività.

In un ambiente di sviluppo locale con un piccolo set di dati e pochissime connessioni, questa migrazione potrebbe sembrare istantanea. Tuttavia, su set di dati di grandi dimensioni con molte connessioni simultanee, ottenere un blocco e creare l'indice può richiedere del tempo.

Nei passaggi successivi, modificherai le migrazioni create da Django per creare l'indice senza causare tempi di inattività.



Migrazione falsa

Il primo approccio consiste nel creare l'indice manualmente. Genererai la migrazione, ma in realtà non lascerai che Django la applichi. Invece, eseguirai manualmente l'SQL nel database e poi farai in modo che Django pensi che la migrazione sia completata.

Innanzitutto, genera la migrazione:

$ python manage.py makemigrations --name add_index_fake
Migrations for 'app':
  app/migrations/0002_add_index_fake.py
    - Alter field sold_at on sale

Usa sqlmigrate comando per visualizzare l'SQL che Django utilizzerà per eseguire questa migrazione:

$ python manage.py sqlmigrate app 0002
BEGIN;
--
-- Alter field sold_at on sale
--
CREATE INDEX "app_sale_sold_at_b9438ae4" ON "app_sale" ("sold_at");
COMMIT;

Si desidera creare l'indice senza bloccare la tabella, quindi è necessario modificare il comando. Aggiungi il CONCURRENTLY parola chiave ed eseguire nel database:

app=# CREATE INDEX CONCURRENTLY "app_sale_sold_at_b9438ae4"
ON "app_sale" ("sold_at");

CREATE INDEX

Nota che hai eseguito il comando senza BEGIN e COMMIT parti. L'omissione di queste parole chiave eseguirà i comandi senza una transazione di database. Discuteremo le transazioni del database più avanti nell'articolo.

Dopo aver eseguito il comando, se provi ad applicare le migrazioni, riceverai il seguente errore:

$ python manage.py migrate

Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0002_add_index_fake...Traceback (most recent call last):
  File "venv/lib/python3.7/site-packages/django/db/backends/utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)

psycopg2.ProgrammingError: relation "app_sale_sold_at_b9438ae4" already exists

Django si lamenta che l'indice esiste già, quindi non può procedere con la migrazione. Hai appena creato l'indice direttamente nel database, quindi ora devi fare in modo che Django pensi che la migrazione sia già stata applicata.

Come falsificare una migrazione

Django fornisce un modo integrato per contrassegnare le migrazioni come eseguite, senza eseguirle effettivamente. Per utilizzare questa opzione, imposta il --fake flag quando si applica la migrazione:

$ python manage.py migrate --fake
Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0002_add_index_fake... FAKED

Django non ha sollevato un errore questa volta. In effetti, Django non ha applicato alcuna migrazione. L'ha semplicemente contrassegnato come eseguito (o FAKED ).

Ecco alcuni problemi da considerare quando si falsificano le migrazioni:

  • Il comando manuale deve essere equivalente all'SQL generato da Django: Devi assicurarti che il comando che esegui sia equivalente all'SQL generato da Django. Usa sqlmigrate per produrre il comando SQL. Se i comandi non corrispondono, potresti ritrovarti con incongruenze tra il database e lo stato dei modelli.

  • Anche altre migrazioni non applicate verranno falsificate: Quando hai più migrazioni non applicate, saranno tutte false. Prima di applicare le migrazioni, è importante assicurarsi che solo le migrazioni che desideri falsificare non siano applicate. Altrimenti, potresti ritrovarti con delle incongruenze. Un'altra opzione è specificare l'esatta migrazione che vuoi falsificare.

  • È richiesto l'accesso diretto al database: È necessario eseguire il comando SQL nel database. Questa non è sempre un'opzione. Inoltre, eseguire comandi direttamente in un database di produzione è pericoloso e dovrebbe essere evitato quando possibile.

  • I processi di distribuzione automatizzati potrebbero richiedere modifiche: Se hai automatizzato il processo di distribuzione (utilizzando CI, CD o altri strumenti di automazione), potrebbe essere necessario modificare il processo per simulare migrazioni. Questo non è sempre auspicabile.

Pulizia

Prima di passare alla sezione successiva, è necessario riportare il database al suo stato subito dopo la migrazione iniziale. Per farlo, torna alla migrazione iniziale:

$ python manage.py migrate 0001
Operations to perform:
  Target specific migration: 0001_initial, from app
Running migrations:
  Rendering model states... DONE
  Unapplying app.0002_add_index_fake... OK

Django ha annullato le modifiche apportate nella seconda migrazione, quindi ora è possibile eliminare anche il file:

$ rm app/migrations/0002_add_index_fake.py

Per assicurarti di aver fatto tutto bene, controlla le migrazioni:

$ python manage.py showmigrations app
app
 [X] 0001_initial

È stata applicata la migrazione iniziale e non sono presenti migrazioni non applicate.



Esegui Raw SQL nelle migrazioni

Nella sezione precedente, hai eseguito SQL direttamente nel database e simulato la migrazione. Questo fa il lavoro, ma c'è una soluzione migliore.

Django fornisce un modo per eseguire SQL grezzo nelle migrazioni utilizzando RunSQL . Proviamo ad usarlo invece di eseguire il comando direttamente nel database.

Innanzitutto, genera una nuova migrazione vuota:

$ python manage.py makemigrations app --empty --name add_index_runsql
Migrations for 'app':
  app/migrations/0002_add_index_runsql.py

Quindi, modifica il file di migrazione e aggiungi un RunSQL operazione:

# migrations/0002_add_index_runsql.py

from django.db import migrations, models

class Migration(migrations.Migration):
    atomic = False

    dependencies = [
        ('app', '0001_initial'),
    ]

    operations = [
        migrations.RunSQL(
            'CREATE INDEX "app_sale_sold_at_b9438ae4" '
            'ON "app_sale" ("sold_at");',
        ),
    ]

Quando esegui la migrazione, otterrai il seguente output:

$ python manage.py migrate
Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0002_add_index_runsql... OK

Sembra buono, ma c'è un problema. Proviamo a generare nuovamente le migrazioni:

$ python manage.py makemigrations --name leftover_migration
Migrations for 'app':
  app/migrations/0003_leftover_migration.py
    - Alter field sold_at on sale

Django ha generato di nuovo la stessa migrazione. Perché l'ha fatto?

Pulizia

Prima di poter rispondere a questa domanda, è necessario ripulire e annullare le modifiche apportate al database. Inizia eliminando l'ultima migrazione. Non è stato applicato, quindi è sicuro eliminare:

$ rm app/migrations/0003_leftover_migration.py

Quindi, elenca le migrazioni per l'app app:

$ python manage.py showmigrations app
app
 [X] 0001_initial
 [X] 0002_add_index_runsql

La terza migrazione è andata, ma la seconda è stata applicata. Vuoi tornare nello stato subito dopo la migrazione iniziale. Prova a migrare di nuovo alla migrazione iniziale come hai fatto nella sezione precedente:

$ python manage.py migrate app 0001
Operations to perform:
  Target specific migration: 0001_initial, from app
Running migrations:
  Rendering model states... DONE
  Unapplying app.0002_add_index_runsql...Traceback (most recent call last):

NotImplementedError: You cannot reverse this operation

Django non è in grado di annullare la migrazione.



Operazione di migrazione inversa

Per annullare una migrazione, Django esegue un'azione opposta per ogni operazione. In questo caso, il contrario dell'aggiunta di un indice consiste nell'eliminarlo. Come hai già visto, quando una migrazione è reversibile, puoi annullarla. Proprio come puoi usare checkout in Git, puoi annullare una migrazione se esegui migrate a una migrazione precedente.

Molte operazioni di migrazione integrate definiscono già un'azione inversa. Ad esempio, l'azione inversa per aggiungere un campo consiste nell'eliminare la colonna corrispondente. L'azione inversa per la creazione di un modello consiste nell'eliminare la tabella corrispondente.

Alcune operazioni di migrazione non sono reversibili. Ad esempio, non esiste un'azione inversa per la rimozione di un campo o l'eliminazione di un modello, perché una volta applicata la migrazione, i dati sono spariti.

Nella sezione precedente, hai usato RunSQL operazione. Quando hai provato a invertire la migrazione, hai riscontrato un errore. In base all'errore, una delle operazioni nella migrazione non può essere annullata. Django non è in grado di invertire l'SQL grezzo per impostazione predefinita. Poiché Django non è a conoscenza di ciò che è stato eseguito dall'operazione, non può generare automaticamente un'azione opposta.

Come rendere reversibile una migrazione

Affinché una migrazione sia reversibile, tutte le operazioni in essa contenute devono essere reversibili. Non è possibile annullare parte di una migrazione, quindi una singola operazione non reversibile renderà l'intera migrazione irreversibile.

Per creare un RunSQL operazione reversibile, è necessario fornire SQL da eseguire quando l'operazione viene annullata. L'SQL inverso è fornito in reverse_sql argomento.

L'azione opposta all'aggiunta di un indice è eliminarlo. Per rendere reversibile la migrazione, fornisci il reverse_sql per eliminare l'indice:

# migrations/0002_add_index_runsql.py

from django.db import migrations, models

class Migration(migrations.Migration):
    atomic = False

    dependencies = [
        ('app', '0001_initial'),
    ]

    operations = [
        migrations.RunSQL(
            'CREATE INDEX "app_sale_sold_at_b9438ae4" '
            'ON "app_sale" ("sold_at");',

            reverse_sql='DROP INDEX "app_sale_sold_at_b9438ae4";',
        ),
    ]

Ora prova a invertire la migrazione:

$ python manage.py showmigrations app
app
 [X] 0001_initial
 [X] 0002_add_index_runsql

$ python manage.py migrate app 0001
Operations to perform:
  Target specific migration: 0001_initial, from app
Running migrations:
  Rendering model states... DONE
 Unapplying app.0002_add_index_runsql... OK

$ python manage.py showmigrations app
app
 [X] 0001_initial
 [ ] 0002_add_index_runsql

La seconda migrazione è stata annullata e l'indice è stato eliminato da Django. Ora è possibile eliminare il file di migrazione:

$ rm app/migrations/0002_add_index_runsql.py

È sempre una buona idea fornire reverse_sql . Nelle situazioni in cui l'annullamento di un'operazione SQL grezza non richiede alcuna azione, è possibile contrassegnare l'operazione come reversibile utilizzando la speciale sentinella migrations.RunSQL.noop :

migrations.RunSQL(
    sql='...',  # Your forward SQL here
    reverse_sql=migrations.RunSQL.noop,
),


Capire lo stato del modello e lo stato del database

Nel tuo precedente tentativo di creare l'indice manualmente usando RunSQL , Django ha generato la stessa migrazione più e più volte anche se l'indice è stato creato nel database. Per capire perché Django lo ha fatto, devi prima capire come Django decide quando generare nuove migrazioni.


Quando Django genera una nuova migrazione

Nel processo di generazione e applicazione delle migrazioni, Django esegue la sincronizzazione tra lo stato del database e lo stato dei modelli. Ad esempio, quando aggiungi un campo a un modello, Django aggiunge una colonna alla tabella. Quando rimuovi un campo dal modello, Django rimuove la colonna dalla tabella.

Per sincronizzare tra i modelli e il database, Django mantiene uno stato che rappresenta i modelli. Per sincronizzare il database con i modelli, Django genera operazioni di migrazione. Le operazioni di migrazione si traducono in un SQL specifico del fornitore che può essere eseguito nel database. Quando tutte le operazioni di migrazione vengono eseguite, il database ei modelli dovrebbero essere coerenti.

Per ottenere lo stato del database, Django aggrega le operazioni di tutte le migrazioni precedenti. Quando lo stato aggregato delle migrazioni non è coerente con lo stato dei modelli, Django genera una nuova migrazione.

Nell'esempio precedente, hai creato l'indice utilizzando SQL non elaborato. Django non sapeva che hai creato l'indice perché non hai utilizzato un'operazione di migrazione familiare.

Quando Django ha aggregato tutte le migrazioni e le ha confrontate con lo stato dei modelli, ha riscontrato che mancava un indice. Questo è il motivo per cui, anche dopo aver creato l'indice manualmente, Django pensava che mancasse e ha generato una nuova migrazione per esso.



Come separare database e stato nelle migrazioni

Poiché Django non è in grado di creare l'indice nel modo desiderato, si desidera fornire il proprio SQL ma comunque far sapere a Django che l'hai creato.

In altre parole, è necessario eseguire qualcosa nel database e fornire a Django l'operazione di migrazione per sincronizzare il suo stato interno. Per farlo, Django ci fornisce una speciale operazione di migrazione chiamata SeparateDatabaseAndState . Questa operazione non è molto nota e dovrebbe essere riservata a casi speciali come questo.

È molto più facile modificare le migrazioni che scriverle da zero, quindi inizia generando una migrazione nel solito modo:

$ python manage.py makemigrations --name add_index_separate_database_and_state

Migrations for 'app':
  app/migrations/0002_add_index_separate_database_and_state.py
    - Alter field sold_at on sale

Questo è il contenuto della migrazione generata da Django, come prima:

# migrations/0002_add_index_separate_database_and_state.py

from django.db import migrations, models

class Migration(migrations.Migration):

    dependencies = [
        ('app', '0001_initial'),
    ]

    operations = [
        migrations.AlterField(
            model_name='sale',
            name='sold_at',
            field=models.DateTimeField(
                auto_now_add=True,
                db_index=True,
            ),
        ),
    ]

Django ha generato un AlterField operazione sul campo sold_at . L'operazione creerà un indice e aggiornerà lo stato. Vogliamo mantenere questa operazione ma fornire un comando diverso da eseguire nel database.

Ancora una volta, per ottenere il comando, usa l'SQL generato da Django:

$ python manage.py sqlmigrate app 0002
BEGIN;
--
-- Alter field sold_at on sale
--
CREATE INDEX "app_sale_sold_at_b9438ae4" ON "app_sale" ("sold_at");
COMMIT;

Aggiungi il CONCURRENTLY parola chiave nel posto appropriato:

CREATE INDEX CONCURRENTLY "app_sale_sold_at_b9438ae4"
ON "app_sale" ("sold_at");

Quindi, modifica il file di migrazione e usa SeparateDatabaseAndState per fornire il comando SQL modificato per l'esecuzione:

# migrations/0002_add_index_separate_database_and_state.py

from django.db import migrations, models

class Migration(migrations.Migration):

    dependencies = [
        ('app', '0001_initial'),
    ]

    operations = [

        migrations.SeparateDatabaseAndState(

            state_operations=[
                migrations.AlterField(
                    model_name='sale',
                    name='sold_at',
                    field=models.DateTimeField(
                        auto_now_add=True,
                        db_index=True,
                    ),
                ),
            ],

            database_operations=[
                migrations.RunSQL(sql="""
                    CREATE INDEX CONCURRENTLY "app_sale_sold_at_b9438ae4"
                    ON "app_sale" ("sold_at");
                """, reverse_sql="""
                    DROP INDEX "app_sale_sold_at_b9438ae4";
                """),
            ],
        ),

    ],

L'operazione di migrazione SeparateDatabaseAndState accetta 2 elenchi di operazioni:

  1. state_operazioni sono operazioni da applicare sullo stato del modello interno. Non influiscono sul database.
  2. database_operazioni sono operazioni da applicare al database.

Hai mantenuto l'operazione originale generata da Django in state_operations . Quando si utilizza SeparateDatabaseAndState , questo è ciò che di solito vorresti fare. Nota che db_index=True argomento viene fornito al campo. Questa operazione di migrazione farà sapere a Django che c'è un indice sul campo.

Hai usato l'SQL generato da Django e hai aggiunto il CONCURRENTLY parola chiave. Hai usato l'azione speciale RunSQL per eseguire SQL grezzo durante la migrazione.

Se provi a eseguire la migrazione, otterrai il seguente output:

$ python manage.py migrate app
Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0002_add_index_separate_database_and_state...Traceback (most recent call last):
  File "/venv/lib/python3.7/site-packages/django/db/backends/utils.py", line 83, in _execute
    return self.cursor.execute(sql)
psycopg2.InternalError: CREATE INDEX CONCURRENTLY cannot run inside a transaction block



Migrazioni non atomiche

In SQL, CREATE , DROP , ALTER e TRUNCATE le operazioni sono denominate Data Definition Language (DDL). Nei database che supportano DDL transazionale, come PostgreSQL, Django esegue le migrazioni all'interno di una transazione di database per impostazione predefinita. Tuttavia, in base all'errore sopra riportato, PostgreSQL non può creare un indice contemporaneamente all'interno di un blocco di transazione.

Per poter creare un indice contemporaneamente all'interno di una migrazione, è necessario indicare a Django di non eseguire la migrazione in una transazione di database. Per farlo, contrassegni la migrazione come non atomica impostando atomic a False :

# migrations/0002_add_index_separate_database_and_state.py

from django.db import migrations, models

class Migration(migrations.Migration):
    atomic = False

    dependencies = [
        ('app', '0001_initial'),
    ]

    operations = [

        migrations.SeparateDatabaseAndState(

            state_operations=[
                migrations.AlterField(
                    model_name='sale',
                    name='sold_at',
                    field=models.DateTimeField(
                        auto_now_add=True,
                        db_index=True,
                    ),
                ),
            ],

            database_operations=[
                migrations.RunSQL(sql="""
                    CREATE INDEX CONCURRENTLY "app_sale_sold_at_b9438ae4"
                    ON "app_sale" ("sold_at");
                """,
                reverse_sql="""
                    DROP INDEX "app_sale_sold_at_b9438ae4";
                """),
            ],
        ),

    ],

Dopo aver contrassegnato la migrazione come non atomica, puoi eseguire la migrazione:

$ python manage.py migrate app
Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0002_add_index_separate_database_and_state... OK

Hai appena eseguito la migrazione senza causare tempi di inattività.

Ecco alcuni problemi da considerare quando utilizzi SeparateDatabaseAndState :

  • Le operazioni di database devono essere equivalenti alle operazioni di stato: Le incoerenze tra il database e lo stato del modello possono causare molti problemi. Un buon punto di partenza è mantenere le operazioni generate da Django in state_operations e modifica l'output di sqlmigrate da utilizzare in database_operations .

  • Le migrazioni non atomiche non possono essere ripristinate in caso di errore: Se si verifica un errore durante la migrazione, non sarà possibile eseguire il rollback. Dovresti annullare la migrazione o completarla manualmente. È una buona idea ridurre al minimo le operazioni eseguite all'interno di una migrazione non atomica. Se hai operazioni aggiuntive nella migrazione, spostale in una nuova migrazione.

  • La migrazione potrebbe essere specifica del fornitore: L'SQL generato da Django è specifico del database backend utilizzato nel progetto. Potrebbe funzionare con altri backend di database, ma non è garantito. Se devi supportare più backend di database, devi apportare alcune modifiche a questo approccio.



Conclusione

Hai iniziato questo tutorial con una tabella grande e un problema. Volevi rendere la tua app più veloce per i tuoi utenti e volevi farlo senza causare loro tempi di inattività.

Alla fine del tutorial, sei riuscito a generare e modificare in sicurezza una migrazione Django per raggiungere questo obiettivo. Hai affrontato diversi problemi lungo il percorso e sei riuscito a superarli utilizzando gli strumenti integrati forniti dal framework delle migrazioni.

In questo tutorial, hai imparato quanto segue:

  • Come funzionano internamente le migrazioni Django utilizzando il modello e lo stato del database e quando vengono generate nuove migrazioni
  • Come eseguire SQL personalizzato nelle migrazioni utilizzando RunSQL azione
  • Cosa sono le migrazioni reversibili e come creare un RunSQL azione reversibile
  • Cosa sono le migrazioni atomiche e come modificare il comportamento predefinito in base alle tue esigenze
  • Come eseguire in sicurezza migrazioni complesse in Django

La separazione tra il modello e lo stato del database è un concetto importante. Una volta compreso e come utilizzarlo, puoi superare molti limiti delle operazioni di migrazione integrate. Alcuni casi d'uso che vengono in mente includono l'aggiunta di un indice già creato nel database e la fornitura di argomenti specifici del fornitore ai comandi DDL.