# Documentazione Django 5.2.8

# Come creare migrazioni di database

Questo documento spiega come strutturare e scrivere migrazioni di database per diversi scenari che potresti incontrare. Per materiale introduttivo sulle migrazioni, vedere the topic guide.

## Migrazioni dati e database multipli

Quando usi più database, potresti aver bisogno di capire se è necessario utilizzare una migrazione per un particolare database. Per esempio, puoi voler lanciare solo una migrazione verso uno specifico database.

Per fare ciò è possibile controllare l’alias della connessione del database all’interno di un’operazione `RunPython` guardando l’attributo `schema_editor.connection.alias`:

    from django.db import migrations

    def forwards(apps, schema_editor):
        if schema_editor.connection.alias != "default":
            return
        # Your migration code goes here

    class Migration(migrations.Migration):
        dependencies = [
            # Dependencies to other migrations
        ]

        operations = [
            migrations.RunPython(forwards),
        ]

Puoi anche fornire suggerimenti che verranno passati al metodo `allow_migrate()` del router di database come `**hints`:

`myapp/dbrouters.py`

    class MyRouter:
        def allow_migrate(self, db, app_label, model_name=None, **hints):
            if "target_db" in hints:
                return db == hints["target_db"]
            return True

Quindi, per sfruttare questo nelle tue migrazioni, procedi come segue:

    from django.db import migrations

    def forwards(apps, schema_editor):
        # Your migration code goes here
        ...

    class Migration(migrations.Migration):
        dependencies = [
            # Dependencies to other migrations
        ]

        operations = [
            migrations.RunPython(forwards, hints={"target_db": "default"}),
        ]

Se la tua operazione `RunPython` o `RunSQL` interessa solo un modello, è buona norma passare `model_name` come suggerimento per renderlo il più trasparente possibile al router. Ciò è particolarmente importante per le app riutilizzabili e di terze parti.

## Migrazioni che aggiungono campi univoci

L’applicazione di una migrazione «plain» che aggiunge un campo univoco non annullabile a una tabella con righe esistenti genererà un errore poiché il valore utilizzato per popolare le righe esistenti viene generato una sola volta, interrompendo così il vincolo univoco.

Pertanto, è necessario eseguire i seguenti passaggi. In questo esempio, aggiungeremo un non-nullable `UUIDField` con un valore predefinito. Modifica il rispettivo campo in base alle tue esigenze.

- Aggiungi il campo al tuo modello con gli argomenti `default=uuid.uuid4` e `unique=True` (scegli un valore predefinito appropriato per il tipo di campo che stai aggiungendo).

- Esegui il comando `makemigrations`. Questo dovrebbe generare una migrazione con un’operazione `AddField`.

- Genera due file di migrazione vuoti per la stessa app eseguendo due volte `makemigrations myapp --empty`. Abbiamo rinominato i file di migrazione per assegnare loro nomi significativi negli esempi seguenti.

- Copia l’operazione `AddField` dalla migrazione generata automaticamente (il primo dei tre nuovi file) all’ultima migrazione, cambia `AddField` in `AlterField` e aggiungi le importazioni di `uuid` e `models`. Per esempio:

  `0006_remove_uuid_null.py`

      # Generated by Django A.B on YYYY-MM-DD HH:MM
      from django.db import migrations, models
      import uuid

      class Migration(migrations.Migration):
          dependencies = [
              ("myapp", "0005_populate_uuid_values"),
          ]

          operations = [
              migrations.AlterField(
                  model_name="mymodel",
                  name="uuid",
                  field=models.UUIDField(default=uuid.uuid4, unique=True),
              ),
          ]

- Modifica il primo file di migrazione. La classe di migrazione generata sarà simile a questa:

  `0004_add_uuid_field.py`

      class Migration(migrations.Migration):
          dependencies = [
              ("myapp", "0003_auto_20150129_1705"),
          ]

          operations = [
              migrations.AddField(
                  model_name="mymodel",
                  name="uuid",
                  field=models.UUIDField(default=uuid.uuid4, unique=True),
              ),
          ]

  Cambia `unique=True` in `null=True` – questo creerà il campo null intermedio e rinvierà la creazione del vincolo univoco fino a quando non avremo popolato valori univoci su tutte le righe.

- Nel primo file di migrazione vuoto, aggiungi una operazione `RunPython` o `RunSQL` per generare un valore univoco (UUID nell’esempio) per ogni riga esistente. Aggiungi anche un import di `uuid`. Per esempio:

  `0005_populate_uuid_values.py`

      # Generated by Django A.B on YYYY-MM-DD HH:MM
      from django.db import migrations
      import uuid

      def gen_uuid(apps, schema_editor):
          MyModel = apps.get_model("myapp", "MyModel")
          for row in MyModel.objects.all():
              row.uuid = uuid.uuid4()
              row.save(update_fields=["uuid"])

      class Migration(migrations.Migration):
          dependencies = [
              ("myapp", "0004_add_uuid_field"),
          ]

          operations = [
              # omit reverse_code=... if you don't want the migration to be reversible.
              migrations.RunPython(gen_uuid, reverse_code=migrations.RunPython.noop),
          ]

- Ora puoi applicare le migrazioni come al solito con il comando `migrate` .

  Nota che si crea una race condition se permetti agli oggetti di essere creati mentre la migrazione gira. Gli oggetti creati dopo `AddField` e prima di `RunPython` avranno il loro `uuid` originale sovrascritto.

### Migrazioni «Non-atomic»

In un database che supporta le transazioni DDL (SQLite e PostgreSQL), le migrations sono eseguite di default all’interno di una transazione. Per casi d’uso quali migrations su grandi tabelle, potresti voler evitare l’esecuzione delle migrations all’interno di una transazione, configurando l’attributo `atomic` a `False`:

    from django.db import migrations

    class Migration(migrations.Migration):
        atomic = False

In tale migrazione, tutte le operazioni sono lanciate senza transazione. E” possibile eseguire parti della migrazione dentro una transazione usando `atomic()` o passando `atomic=True` a `RunPython`.

Qui c’è un esempio di migrazione dei dati non atomica che aggiorna una grande tabella con piccoli passi automatici:

    import uuid

    from django.db import migrations, transaction

    def gen_uuid(apps, schema_editor):
        MyModel = apps.get_model("myapp", "MyModel")
        while MyModel.objects.filter(uuid__isnull=True).exists():
            with transaction.atomic():
                for row in MyModel.objects.filter(uuid__isnull=True)[:1000]:
                    row.uuid = uuid.uuid4()
                    row.save()

    class Migration(migrations.Migration):
        atomic = False

        operations = [
            migrations.RunPython(gen_uuid),
        ]

L’attributo `atomic` non ha effetto sui database che non supportano le transazioni DDL (per es. MySQL, Oracle). (Il supporto per le istruzioni atomic DDL di MySQL si riferisce a singole istruzioni, piuttosto che a più istruzioni racchiuse in una transazione sulla quale si può fare roll back.)

## Controllo dell’ordine delle migrazioni

Django determina l’ordine in cui le migrazioni dovrebbero essere applicate non in base al nome del file di ogni migrazione, ma usando un grafo che usa due proprietà sulla classe `Migration`: `dependencies` e `run_before`.

Se hai usato il comando `makemigrations` probabilmente hai già visto `dependencies` in azione perchè le migrazioni auto-generate lo hanno definito come parte del loro processo di creazione.

La proprietà `dependencies` viene dichiarata in questo modo:

    from django.db import migrations

    class Migration(migrations.Migration):
        dependencies = [
            ("myapp", "0123_the_previous_migration"),
        ]

Generalmente questo basta, ma di volta in volta potresti aver bisogno di assicurarti che le tue migrazioni girino prima di altre migrazioni. Questo torna utile, ad esempio, per fare in modo che le migrazioni delle app di terze parti girino dopo i rimpiazzi sul tuo `AUTH_USER_MODEL`.

Per ottenere ciò, inserisci tutte le migrazioni che dovrebbero dipendere dalla tua nell’attributo `run_before` nella tua classe `Migration`:

    class Migration(migrations.Migration):
        ...

        run_before = [
            ("third_party_app", "0001_do_awesome"),
        ]

È preferibile usare `dipendenze` su `run_before` quando possibile. Dovresti usare `run_before` solo se non è desiderabile o non pratico specificare `dependencies` nella migrazione che vuoi eseguire dopo quella che stai scrivendo.

## Migrazione dei dati tra app

È possibile utilizzare una migrazione dei dati per spostare i dati da un’applicazione ad un’altra.

Se prevedi di rimuovere la vecchia app in un secondo momento, dovrai impostare la proprietà `dependencies` in base al fatto che la vecchia app sia installata o meno. Altrimenti, avrai le dipendenze mancanti dopo aver disinstallato la vecchia app. Allo stesso modo, dovrai catturare `LookupError` nella chiamata `apps.get_model()` che recupera i modelli dalla vecchia app. Questo approccio ti consente di distribuire il tuo progetto ovunque senza prima installare e quindi disinstallare la vecchia app.

Ecco un esempio di migrazione:

`myapp/migrations/0124_move_old_app_to_new_app.py`

    from django.apps import apps as global_apps
    from django.db import migrations

    def forwards(apps, schema_editor):
        try:
            OldModel = apps.get_model("old_app", "OldModel")
        except LookupError:
            # The old app isn't installed.
            return

        NewModel = apps.get_model("new_app", "NewModel")
        NewModel.objects.bulk_create(
            NewModel(new_attribute=old_object.old_attribute)
            for old_object in OldModel.objects.all()
        )

    class Migration(migrations.Migration):
        operations = [
            migrations.RunPython(forwards, migrations.RunPython.noop),
        ]
        dependencies = [
            ("myapp", "0123_the_previous_migration"),
            ("new_app", "0001_initial"),
        ]

        if global_apps.is_installed("old_app"):
            dependencies.append(("old_app", "0001_initial"))

Potresti non fare nulla (come nell’esempio sopra) o rimuovere alcuni o tutti i dati dalla nuova applicazione. Regola di conseguenza il secondo argomento dell’operazione `RunPython`.

## Modifica di un `ManyToManyField` per utilizzare un modello `through`

If you change a `ManyToManyField` to use a `through` model, the default migration will delete the existing table and create a new one, losing the existing relations. To avoid this, you can use `SeparateDatabaseAndState` to rename the existing table to the new table name while telling the migration autodetector that the new model has been created. You can check the existing table name and constraint name through `sqlmigrate` or `dbshell`. You can check the new table name with the through model’s `_meta.db_table` property. Your new `through` model should use the same names for the `ForeignKey`s as Django did. Also if it needs any extra fields, they should be added in operations after `SeparateDatabaseAndState`.

Per esempio, se avessimo un modello `Book` con un `ManyToManyField` collegato a `Author`, potremmo aggiungere un modello completo `AuthorBook` con un nuovo campo `is_primary`, come:

    from django.db import migrations, models
    import django.db.models.deletion

    class Migration(migrations.Migration):
        dependencies = [
            ("core", "0001_initial"),
        ]

        operations = [
            migrations.SeparateDatabaseAndState(
                database_operations=[
                    # Old table name from checking with sqlmigrate, new table
                    # name from AuthorBook._meta.db_table.
                    migrations.RunSQL(
                        sql="ALTER TABLE core_book_authors RENAME TO core_authorbook",
                        reverse_sql="ALTER TABLE core_authorbook RENAME TO core_book_authors",
                    ),
                ],
                state_operations=[
                    migrations.CreateModel(
                        name="AuthorBook",
                        fields=[
                            (
                                "id",
                                models.AutoField(
                                    auto_created=True,
                                    primary_key=True,
                                    serialize=False,
                                    verbose_name="ID",
                                ),
                            ),
                            (
                                "author",
                                models.ForeignKey(
                                    on_delete=django.db.models.deletion.DO_NOTHING,
                                    to="core.Author",
                                ),
                            ),
                            (
                                "book",
                                models.ForeignKey(
                                    on_delete=django.db.models.deletion.DO_NOTHING,
                                    to="core.Book",
                                ),
                            ),
                        ],
                        options={
                            "constraints": [
                                models.UniqueConstraint(
                                    fields=["author", "book"],
                                    name="unique_author_book",
                                )
                            ],
                        },
                    ),
                    migrations.AlterField(
                        model_name="book",
                        name="authors",
                        field=models.ManyToManyField(
                            to="core.Author",
                            through="core.AuthorBook",
                        ),
                    ),
                ],
            ),
            migrations.AddField(
                model_name="authorbook",
                name="is_primary",
                field=models.BooleanField(default=False),
            ),
        ]

## Modifica di un modello non gestito in gestito

Se desideri modificare un modello non gestito (`managed=False`) in gestito, devi rimuovere `managed=False` e generare una migrazione prima di creare un altro schema -modifiche correlate al modello, poiché le modifiche allo schema che appaiono nella migrazione che contiene l’operazione di modifica di `Meta.managed` potrebbero non essere applicate.