---
title: "Opérations de migration"
version: 4.2
locale: fr
source: https://docs.djangoproject.com/fr/4.2/ref/migration-operations/
canonical: https://djangodocs.dev/fr/4.2/ref/migration-operations/
---
# Opérations de migration

Les fichiers de migration sont composés de un ou plusieurs objets `Operation` qui enregistrent de manière déclarative les opérations à appliquer à la base de données.

Django utilise également ces objets `Operation` pour recomposer un état historisé de vos modèles et pour calculer les modifications effectuées aux modèles depuis la dernière migration afin de pouvoir écrire automatiquement les migrations ; c’est pourquoi ils sont déclaratifs, ce qui permet à Django de facilement tous les charger en mémoire et de les parcourir sans devoir accéder à la base de données pour trouver à quoi le projet devrait ressembler.

Certains objets `Operation` plus spécialisés sont destinés à des opérations comme les [migrations de données](/fr/4.2/topics/migrations/#data-migrations) et pour des manipulations manuelles avancées de la base de données. Vous pouvez même écrire vos propres classes `Operation` si vous le voulez pour englober des modifications que vous effectuez fréquemment.

Si vous avez besoin d’un fichier de migration vide pour y écrire vos propres objets `Operation`, exécutez `python manage.py makemigrations --empty nom_application`, mais soyez conscient que l’ajout manuel d’opérations modifiant le schéma peut perturber l’autodétecteur de migrations et provoquer du code incorrect durant les prochaines exécutions de [`makemigrations`](/fr/4.2/ref/django-admin/#django-admin-makemigrations).

Toutes les opérations Django de base se trouvent dans le module `django.db.migrations.operations`.

Pour du contenu d’initiation, consultez le [guide thématique des migrations](/fr/4.2/topics/migrations/).

## Opérations liées au schéma

### `CreateModel`

#### `class CreateModel(name, fields, options=None, bases=None, managers=None)`

Crée un nouveau modèle dans l’historique du projet et une table correspondante dans la base de données.

`name` est le nom du modèle tel qu’il est écrit dans le fichier `models.py`.

`fields` est une liste de tuples binaires `(nom_champ, instance_champ)`. L’instance de champ doit être un champ non lié (« unbound ») (donc simplement `models.CharField(…)`, plutôt qu’un champ repris d’un autre modèle).

`options` est un dictionnaire facultatif de valeurs tirées de la classe `Meta` du modèle.

`bases` est une liste facultative d’autres classes dont ce modèle doit hériter ; elle peut contenir aussi bien des objets classes que des chaînes au format `"nomapp.NomModele"` si vous voulez dépendre d’un autre modèle (vous héritez donc de sa version historique). En cas d’absence, la valeur par défaut hérite du modèle standard `models.Model`.

`managers` accepte une liste de tuples binaires `(nom_gestionnaire, instance_gestionnaire)`. Le premier gestionnaire de la liste sera le gestionnaire par daéfut de ce modèle pour les migrations.

### `DeleteModel`

#### `class DeleteModel(name)`

Supprime le modèle de l’historique du projet et ses tables de la base de données.

### `RenameModel`

#### `class RenameModel(old_name, new_name)`

Renomme le modèle d’un ancien nom vers un nouveau nom.

Il peut arriver que vous deviez ajoutez manuellement cette opération si vous modifiez à la fois le nom du modèle et plusieurs de ses champs ; pour l’autodétecteur, il paraîtra que l’ancien modèle a été supprimé et qu’un nouveau modèle a été ajouté avec un nom différent, ce qui fera que la migration créée perdra toutes les données de l’ancienne table.

### `AlterModelTable`

#### `class AlterModelTable(name, table)`

Modifie le nom de la table du modèle (l’option [`db_table`](/fr/4.2/ref/models/options/#django.db.models.Options.db_table) de la classe interne `Meta`).

### `AlterModelTableComment`

> **New in Django 4.2**

#### `class AlterModelTableComment(name, table_comment)`

Modifie le commentaire de la table du modèle (l’option [`db_table_comment`](/fr/4.2/ref/models/options/#django.db.models.Options.db_table_comment) de la classe interne `Meta`).

### `AlterUniqueTogether`

#### `class AlterUniqueTogether(name, unique_together)`

Modifie l’ensemble de contraintes d’unicité du modèle (l’option [`unique_together`](/fr/4.2/ref/models/options/#django.db.models.Options.unique_together) de la classe interne `Meta`).

### `AlterIndexTogether`

#### `class AlterIndexTogether(name, index_together)`

Modifie l’ensemble d’index personnalisés du modèle (l’option `index_together` de la classe interne `Meta`).

> **Warning**
>
> `AlterIndexTogether` n’est officiellement pris en charge que par les fichiers de migration avant Django 4.2. Pour des raisons de rétrocompatibilité, cette opération fait toujours partie de l’API publique, et nous ne prévoyons pas de la rendre obsolète ou de la supprimer, mais elle ne devrait plus être utilisée pour de nouvelles migrations. Utilisez à la place les opérations [`AddIndex`](#django.db.migrations.operations.AddIndex) et [`RemoveIndex`](#django.db.migrations.operations.RemoveIndex).

### `AlterOrderWithRespectTo`

#### `class AlterOrderWithRespectTo(name, order_with_respect_to)`

Crée ou supprime la colonne `_order` nécessaire à l’option [`order_with_respect_to`](/fr/4.2/ref/models/options/#django.db.models.Options.order_with_respect_to) de la classe interne `Meta`.

### `AlterModelOptions`

#### `class AlterModelOptions(name, options)`

Stocke les modifications de diverses options de modèles (attributs de la classe `Meta` d’un modèle) comme `permissions` ou `verbose_name`. N’affecte pas la base de données, mais fait persister ces modifications à l’usage des instances [`RunPython`](#django.db.migrations.operations.RunPython). `options` doit être un dictionnaire faisant correspondre des noms d’options à leurs valeurs.

### `AlterModelManagers`

#### `class AlterModelManagers(name, managers)`

Modifie les gestionnaires disponibles durant les migrations.

### `AddField`

#### `class AddField(model_name, name, field, preserve_default=True)`

Ajoute un champ à un modèle. `model_name` est le nom du modèle, `name` est le nom du champ et `field` est une instance de champ `Field` non liée (ce que vous placeriez dans une déclaration de champ dans `models.py`, par exemple `models.IntegerField(null=True)`).

Le paramètre `preserve_default` indique si la valeur par défaut du champ est permanente et doit être incluse dans l’état du projet (`True`) ou si elle n’est que temporaire et juste pour la migration (`False`), généralement parce que la migration ajoute un champ non nul à une table et qu’elle a besoin d’une valeur par défaut pour remplir les lignes existantes. Cela ne concerne pas le comportement de définition de valeurs par défaut au sein même de la base de données, car Django ne définit jamais de valeurs par défaut pour la base de données et les applique toujours au niveau du code Django de l’ORM.

> **Warning**
>
> Sur les bases de données plus anciennes, l’ajout d’un champ avec valeur par défaut peut provoquer une réécriture complète de la table. Cela arrive même pour des champs avec valeur nulle autorisée et peut affecter les performances en baisse. Pour éviter cela, il s’agit de suivre les étapes suivantes.
>
> - Ajoutez le champ avec valeur nulle autorisée sans valeur par défaut et lancez la commande [`makemigrations`](/fr/4.2/ref/django-admin/#django-admin-makemigrations). Cela devrait générer une migration contenant une opération `AddField`.
> - Ajoutez la valeur par défaut à votre champ et lancez la commande [`makemigrations`](/fr/4.2/ref/django-admin/#django-admin-makemigrations). Cela devrait générer une migration contenant une opération `AlterField`.

### `RemoveField`

#### `class RemoveField(model_name, name)`

Supprime un champ d’un modèle.

Gardez en tête que lors de l’inversion de l’opération, un nouveau champ est ajouté au modèle. L’opération est réversible (sans prendre en compte la perte de données qui est irréversible) à la condition que le champ puisse valoir `null` ou qu’il possède une valeur par défaut utilisable pour remplir la colonne recréée. Dans le cas contraire, l’opération est irréversible.

### `AlterField`

#### `class AlterField(model_name, name, field, preserve_default=True)`

Modifie la définition d’un champ, y compris les modifications de son type, des attributs [`null`](/fr/4.2/ref/models/fields/#django.db.models.Field.null), [`unique`](/fr/4.2/ref/models/fields/#django.db.models.Field.unique), [`db_column`](/fr/4.2/ref/models/fields/#django.db.models.Field.db_column) ou des autres attributs de champ.

Le paramètre `preserve_default` indique si la valeur par défaut du champ est permanente et doit être incluse dans l’état du projet (`True`) ou si elle n’est que temporaire et juste pour la migration (`False`), généralement parce que la migration modifie un champ nul vers un champ non nul à une table et qu’elle a besoin d’une valeur par défaut pour remplir les lignes existantes. Cela ne concerne pas le comportement de définition de valeurs par défaut au sein même de la base de données, car Django ne définit jamais de valeurs par défaut pour la base de données et les applique toujours au niveau du code Django de l’ORM.

Notez qu’il n’est pas toujours possible d’effectuer toutes les modifications, aussi en fonction de la base de données. Par exemple, il n’est pas possible de transformer un champ de type texte comme `models.TextField()` en un champ de type numérique comme `models.IntegerField()` dans la plupart des bases de données.

### `RenameField`

#### `class RenameField(model_name, old_name, new_name)`

Modifie le nom d’un champ (et, si [`db_column`](/fr/4.2/ref/models/fields/#django.db.models.Field.db_column) n’est pas défini, le nom de la colonne).

### `AddIndex`

#### `class AddIndex(model_name, index)`

Crée un index dans la table de base de données pour le modèle `model_name`. `index` est une instance de la classe [`Index`](/fr/4.2/ref/models/indexes/#django.db.models.Index).

### `RemoveIndex`

#### `class RemoveIndex(model_name, name)`

Supprime l’index nommé `name` du modèle ayant le nom `model_name`.

### `RenameIndex`

> **New in Django 4.1**

#### `class RenameIndex(model_name, new_name, old_name=None, old_fields=None)`

Renomme un index dans la table de base de données pour le modèle `model_name`. Un et un seul paramètre `old_name` ou `old_fields` doit être fourni. `old_fields` est un itérable de chaînes, correspondant souvent aux champs dans [`index_together`](/fr/4.2/ref/models/options/#django.db.models.Options.index_together).

Avec les bases de données qui ne gèrent pas les instructions de renommage des index (SQLite et MariaDB \< 10.5.2), l’opération va supprimer puis recréer l’index, ce qui peut être coûteux.

### `AddConstraint`

#### `class AddConstraint(model_name, constraint)`

Crée une :doc:\` contrainte \</ref/models/constraints\>\` dans la table de base de données pour le modèle nommé `model_name`.

### `RemoveConstraint`

#### `class RemoveConstraint(model_name, name)`

Supprime la contrainte nommée `name` du modèle ayant le nom `model_name`.

## Opérations spéciales

### `RunSQL`

#### `class RunSQL(sql, reverse_sql=None, state_operations=None, hints=None, elidable=False)`

Permet d’exécuter du code SQL arbitraire dans la base de données, pratique pour des fonctionnalités plus avancées des moteurs de base de données que Django ne prend pas directement en charge.

`sql`, et `reverse_sql` s’il est fourni, doivent être des chaîne de SQL à exécuter dans la base de données. Pour la plupart des moteurs de base de données (tous sauf PostgreSQL), Django sépare le code SQL en instructions individuelles avant de les exécuter.

> **Warning**
>
> Avec PostgreSQL et SQLite, n’utilisez des instructions SQL `BEGIN` ou `COMMIT` que dans des [migrations non atomiques](/fr/4.2/howto/writing-migrations/#non-atomic-migrations), afin d’éviter de casser l’état de transaction de Django.

Vous pouvez aussi transmettre une liste de chaînes ou de tuples binaires. Cette dernière option est utilisée pour transmettre des requêtes et des paramètres sur le même principe que pour [cursor.execute()](/fr/4.2/topics/db/sql/#executing-custom-sql). Ces trois opérations sont équivalentes :

```
migrations.RunSQL("INSERT INTO musician (name) VALUES ('Reinhardt');")
migrations.RunSQL([("INSERT INTO musician (name) VALUES ('Reinhardt');", None)])
migrations.RunSQL([("INSERT INTO musician (name) VALUES (%s);", ["Reinhardt"])])
```

Si vous voulez inclure des signes « pour cent » littéraux dans la requête, vous devez les doubler si vous transmettez des paramètres.

Les requêtes `reverse_sql` sont exécutées lorsque la migration est défaite. Elles sont censé défaire ce que font les requêtes `sql`. Par exemple, pour annuler l’insertion ci-dessus par une suppression :

```
migrations.RunSQL(
    sql=[("INSERT INTO musician (name) VALUES (%s);", ["Reinhardt"])],
    reverse_sql=[("DELETE FROM musician where name=%s;", ["Reinhardt"])],
)
```

Si `reverse_sql` vaut `None` (par défaut), l’opération `RunSQL` est irréversible.

Le paramètre `state_operations` permet de fournir des opérations qui sont équivalentes au code SQL en terme d’état du projet. Par exemple, si vous créez une colonne manuellement, vous devriez passer une liste contenant une opération `AddField` afin que l’autodétecteur puisse maintenir un état à jour du modèle. Sinon, au prochain lancement de `makemigrations`, il ne verra aucune opération ajoutant ce champ et va donc essayer de le recréer une nouvelle fois. Par exemple :

```
migrations.RunSQL(
    "ALTER TABLE musician ADD COLUMN name varchar(255) NOT NULL;",
    state_operations=[
        migrations.AddField(
            "musician",
            "name",
            models.CharField(max_length=255),
        ),
    ],
)
```

Le paramètre facultatif `hints` sera transmis comme `**hints` à la méthode [`allow_migrate()`](/fr/4.2/topics/db/multi-db/#allow_migrate) des routeurs de base de données afin de les aider à prendre les décisions de routage. Voir [Indications (hints)](/fr/4.2/topics/db/multi-db/#topics-db-multi-db-hints) pour plus de détails sur les indications de base de données.

Le paramètre facultatif `elidable` détermine si l’opération sera supprimée (éludée) lors de la [fusion des migrations](/fr/4.2/topics/migrations/#migration-squashing).

#### `RunSQL.noop`

Transmettez l’attribut `RunSQL.noop` à `sql` ou `reverse_sql` lorsque vous voulez que l’opération ne fasse rien dans une direction donnée. C’est particulièrement utile pour rendre une opération réversible.

### `RunPython`

#### `class RunPython(code, reverse_code=None, atomic=None, hints=None, elidable=False)`

Exécute du code Python personnalisé dans un contexte historisé. `code` (et `reverse_code` s’il est renseigné) doivent être des objets exécutables acceptant deux paramètres ; le premier est une instance de `django.apps.registry.Apps` contenant les modèles historisés qui correspondent à la position de l’opération dans l’historique du projet et la seconde une instance de [`SchemaEditor`](/fr/4.2/ref/schema-editor/#django.db.backends.base.schema.BaseDatabaseSchemaEditor).

Le paramètre `reverse_code` est appelé lors de l’inversion des migrations. Cet objet exécutable doit défaire ce qui a été fait par l’objet exécutable `code` afin que cette migration soit réversible. Si `reverse_code` vaut `None` (par défaut), l’opération `RunPython` est irréversible.

Le paramètre facultatif `hints` sera transmis comme `**hints` à la méthode [`allow_migrate()`](/fr/4.2/topics/db/multi-db/#allow_migrate) des routeurs de base de données afin de les aider à prendre une décision de routage. Voir [Indications (hints)](/fr/4.2/topics/db/multi-db/#topics-db-multi-db-hints) pour plus de détails sur les indications de base de données.

Le paramètre facultatif `elidable` détermine si l’opération sera supprimée (éludée) lors de la [fusion des migrations](/fr/4.2/topics/migrations/#migration-squashing).

Il est conseillé d’écrire le code sous forme de fonction distincte au-dessus de la classe `Migration` dans le fichier de migration et de transmettre celle-ci à `RunPython`. Voici un exemple d’emploi de `RunPython` pour créer quelques objets initiaux d’un modèle `Country`:

```
from django.db import migrations

def forwards_func(apps, schema_editor):
    # We get the model from the versioned app registry;
    # if we directly import it, it'll be the wrong version
    Country = apps.get_model("myapp", "Country")
    db_alias = schema_editor.connection.alias
    Country.objects.using(db_alias).bulk_create(
        [
            Country(name="USA", code="us"),
            Country(name="France", code="fr"),
        ]
    )

def reverse_func(apps, schema_editor):
    # forwards_func() creates two Country instances,
    # so reverse_func() should delete them.
    Country = apps.get_model("myapp", "Country")
    db_alias = schema_editor.connection.alias
    Country.objects.using(db_alias).filter(name="USA", code="us").delete()
    Country.objects.using(db_alias).filter(name="France", code="fr").delete()

class Migration(migrations.Migration):
    dependencies = []

    operations = [
        migrations.RunPython(forwards_func, reverse_func),
    ]
```

Il s’agit généralement de l’opération que l’on utilise pour créer des [migrations de données](/fr/4.2/topics/migrations/#data-migrations), pour lancer des mises à jour et modifications de données personnalisées ou pour toute autre opération qui nécessite le recours à l’ORM ou à du code Python.

Tout comme pour [`RunSQL`](#django.db.migrations.operations.RunSQL), si vous faites ici des modifications au schéma, prenez soin de le faire en dehors de la portée du systèmes des modèles Django (par ex. avec des déclencheurs) ou utilisez [`SeparateDatabaseAndState`](#django.db.migrations.operations.SeparateDatabaseAndState) pour insérer des opérations qui reflètent vos modifications sur l’état des modèles. Sinon, l’ORM versionné et l’autodétecteur risquent de ne plus fonctionner correctement.

Par défaut, `RunPython` va exécuter son contenu à l’intérieur d’une transaction pour les bases de données qui ne prennent pas en charge les transactions DDL (instructions de modification du schéma), par exemple MySQL ou Oracle. Cela se passe en principe bien, mais pourrait provoquer un plantage si vous essayez d’utiliser l’objet  `schema_editor` fournit par ces moteurs ; dans ce cas, passez `atomic=False` à l’opération `RunPython`.

Pour les bases de données qui prennent en charge les transactions de schéma DDL (SQLite et PostgreSQL), aucune transaction n’est automatiquement rajoutée pour les opérations `RunPython` en dehors des transactions créées pour chaque migration. Ainsi, par exemple avec PostgreSQL, vous devriez éviter de combiner des modifications de schéma avec des opérations `RunPython` dans la même migration, au risque de recevoir des erreurs du genre `OperationalError: cannot ALTER TABLE "mytable" because it has pending trigger events`.

Si votre base de données est différente et que vous n’êtes pas sûr si elle prend en charge les transactions DDL, consultez l’attribut `django.db.connection.features.can_rollback_ddl`.

Si l’opération `RunPython` fait partie d’une [migration non atomique](/fr/4.2/howto/writing-migrations/#non-atomic-migrations), l’opération ne sera exécutée dans une transaction que si `atomic=True` est transmis à l’opération `RunPython`.

> **Warning**
>
> `RunPython` ne modifie pas de façon magique la connexion des modèles à votre place ; toute méthode de modèle que vous appelez sera dirigée vers la base de données par défaut, sauf si vous leur indiquez l’alias de base de données à utiliser (disponible dans `schema_editor.connection.alias`, où `schema_editor` est le second paramètre reçu par votre fonction).

#### `static RunPython.noop()`

Transmettez la méthode `RunPython.noop` à `code` ou `reverse_code` lorsque vous voulez que l’opération ne fasse rien dans une direction donnée. C’est particulièrement utile pour rendre une opération réversible.

### `SeparateDatabaseAndState`

#### `class SeparateDatabaseAndState(database_operations=None, state_operations=None)`

Une opération hautement spécialisée qui permet de mélanger des opérations touchant aux aspects de modification de schéma de base de données avec des opérations de changement d’état (en lien avec l’autodétecteur).

Elle accepte deux listes d’opérations. Quand on lui demande d’appliquer les changements d’état, elle utilise la liste `state_operations` (il s’agit d’une version généralisée du paramètre `state_operations` de [`RunSQL`](#django.db.migrations.operations.RunSQL)). Quand on lui demande d’appliquer les modifications de base de données, elle utilise la liste `database_operations`.

Si l’état actuel de la base de données et la vue de cet état par Django diffèrent, ceci peut casser le système des migrations, voire même aboutir à des pertes de données. Il vaut la peine de faire bien attention et de contrôler attentivement les opérations de base de données et d’état. Vous pouvez utiliser [`sqlmigrate`](/fr/4.2/ref/django-admin/#django-admin-sqlmigrate) et [`dbshell`](/fr/4.2/ref/django-admin/#django-admin-dbshell) pour vérifier les opérations de base de données. Vous pouvez utiliser [`makemigrations`](/fr/4.2/ref/django-admin/#django-admin-makemigrations), particulièrement avec l’option [`--dry-run`](/fr/4.2/ref/django-admin/#cmdoption-makemigrations-dry-run), pour vérifier les opérations d’état.

Pour voir un exemple qui utilise `SeparateDatabaseAndState`, consultez [Modification d’un champ ManyToManyField pour utiliser un modèle intermédiaire](/fr/4.2/howto/writing-migrations/#changing-a-manytomanyfield-to-use-a-through-model).

## Écriture d’opérations personnalisées

Les opérations possèdent une API relativement simple et sont conçues pour pouvoir facilement écrire des sous-classes pour compléter celles que Django fournit. La structure de base d’une `Operation` ressemble à ceci :

```
from django.db.migrations.operations.base import Operation

class MyCustomOperation(Operation):
    # If this is False, it means that this operation will be ignored by
    # sqlmigrate; if true, it will be run and the SQL collected for its output.
    reduces_to_sql = False

    # If this is False, Django will refuse to reverse past this operation.
    reversible = False

    def __init__(self, arg1, arg2):
        # Operations are usually instantiated with arguments in migration
        # files. Store the values of them on self for later use.
        pass

    def state_forwards(self, app_label, state):
        # The Operation should take the 'state' parameter (an instance of
        # django.db.migrations.state.ProjectState) and mutate it to match
        # any schema changes that have occurred.
        pass

    def database_forwards(self, app_label, schema_editor, from_state, to_state):
        # The Operation should use schema_editor to apply any changes it
        # wants to make to the database.
        pass

    def database_backwards(self, app_label, schema_editor, from_state, to_state):
        # If reversible is True, this is called when the operation is reversed.
        pass

    def describe(self):
        # This is used to describe what the operation does in console output.
        return "Custom Operation"

    @property
    def migration_name_fragment(self):
        # Optional. A filename part suitable for automatically naming a
        # migration containing this operation, or None if not applicable.
        return "custom_operation_%s_%s" % (self.arg1, self.arg2)
```

Vous pouvez prendre ce modèle et l’étendre, même si nous suggérons d’examiner les opérations fournies par Django dans `django.db.migrations.operations`. Elles couvrent une grande partie des exemples d’utilisation des aspects semi-internes du système des migrations, tels que `ProjectState` ainsi que les mécanismes utilisés pour obtenir les modèles historisés, tout comme `ModelState` et les motifs utilisés pour faire évoluer les modèles historisés dans `state_forwards()`.

Quelques éléments à signaler :

- Vous n’avez pas besoin d’en apprendre beaucoup sur `ProjectState` pour écrire des migrations ; il suffit de savoir qu’il possède une propriété `apps` qui donne accès à un registre d’applications (sur lequel vous pouvez appeler `get_model`).
- `database_forwards` et `database_backwards` reçoivent tous deux deux états ; ils représentent la différence par rapport à ce que la méthode `state_forwards` aurait appliqué, mais vous sont transmis par commodité et pour des raisons de vitesse.
- Si vous souhaitez manipuler des classes ou instances de modèles à partir du paramètre `from_state` dans `database_forwards()` ou `database_backwards()`, il faut produire les états de modèles en utilisant la méthode `clear_delayed_apps_cache()` afin de rendre disponible les modèles liés :

  ```
  def database_forwards(self, app_label, schema_editor, from_state, to_state):
      # This operation should have access to all models. Ensure that all models are
      # reloaded in case any are delayed.
      from_state.clear_delayed_apps_cache()
      ...
  ```
- `to_state` dans la méthode `database_backwards` est l’état *plus ancien* ; c’est-à-dire celui qui représentera l’état actuel au moment où la migration aura terminé son travail d’annulation.
- Vous pouvez trouver des implémentations de `references_model` dans les opérations fournies par Django ; cela fait partie du code d’autodétection et ne joue pas de rôle pour les opérations personnalisées.

> **Warning**
>
> Pour des raisons de performance, les instances [`Field`](/fr/4.2/ref/models/fields/#django.db.models.Field) dans `ModelState.fields` sont réutilisées d’une migration à l’autre. Vous ne devez jamais toucher aux attributs de ces instances. Si vous avez besoin de faire évoluer un champ dans `state_forwards()`, vous devez enlever l’ancienne instance de `ModelState.fields` et ajouter une nouvelle instance à sa place. Le même principe vaut pour les instances [`Manager`](/fr/4.2/topics/db/managers/#django.db.models.Manager) dans `ModelState.managers`.

Comme exemple, créons une opération qui charge des extensions PostgreSQL (qui contiennent certaines des fonctionnalités les plus enthousiasmantes de PostgreSQL. Comme il n’y a pas de changement d’état de modèle, tout ce qu’elle fait, c’est d’exécuter une commande :

```
from django.db.migrations.operations.base import Operation

class LoadExtension(Operation):
    reversible = True

    def __init__(self, name):
        self.name = name

    def state_forwards(self, app_label, state):
        pass

    def database_forwards(self, app_label, schema_editor, from_state, to_state):
        schema_editor.execute("CREATE EXTENSION IF NOT EXISTS %s" % self.name)

    def database_backwards(self, app_label, schema_editor, from_state, to_state):
        schema_editor.execute("DROP EXTENSION %s" % self.name)

    def describe(self):
        return "Creates extension %s" % self.name

    @property
    def migration_name_fragment(self):
        return "create_extension_%s" % self.name
```
