# Documentation Django 6.0.7

# Sérialisation d’objets Django

L’infrastructure de sérialisation de Django fournit un mécanisme pour « traduire » les modèles Django en d’autres formats. En général, ces autres formats sont basés sur du texte et utilisés pour envoyer des données de Django à travers le réseau, mais il est possible qu’un sérialiseur prenne en charge n’importe quel format (basé sur du texte ou non).

Voir aussi

Si vous voulez simplement extraire certaines données de vos tables pour les sérialiser, il est possible d’utiliser la commande de gestion `dumpdata`.

## Sérialisation de données

Au plus haut niveau, les données peuvent être sérialisées comme ceci :

    from django.core import serializers

    data = serializers.serialize("json", SomeModel.objects.all())

Les paramètres de la fonction `serialize` sont constitués du format dans lequel la sérialisation des données doit se faire (voir Formats de sérialisation) et d’un objet `QuerySet` contenant les données (en fait, le second paramètre peut être n’importe quel itérateur produisant des instances de modèles Django, mais il s’agit presque toujours d’un `QuerySet`).

django.core.serializers.get_serializer(format)

Il est aussi possible d’utiliser directement un objet de sérialiseur :

    JSONSerializer = serializers.get_serializer("json")
    json_serializer = JSONSerializer()
    json_serializer.serialize(queryset)
    data = json_serializer.getvalue()

C’est utile lorsque l’on veut sérialiser des données directement dans un objet de type fichier (ce qui inclut aussi `HttpResponse`) :

    with open("file.json", "w") as out:
        json_serializer.serialize(SomeModel.objects.all(), stream=out)

Note

Si vous appelez `get_serializer()` avec un format de sérialisation inconnu, une exception `django.core.serializers.SerializerDoesNotExist` est générée.

### Sous-ensemble de champs

Lorsqu’on ne veut sérialiser qu’un sous-ensemble de champs, il est possible de renseigner le paramètre `fields` du sérialiseur :

    from django.core import serializers

    data = serializers.serialize("json", SomeModel.objects.all(), fields=["name", "size"])

Dans cet exemple, seuls les attributs `name` et `size` de chaque modèle seront sérialisés. La clé primaire est toujours sérialisée comme élément `pk` dans le résultat ; elle n’apparaît jamais dans la partie `fields`.

Note

Selon votre modèle, il n’est parfois pas possible de désérialiser un modèle qui n’a été sérialisé qu’avec un sous-ensemble de ses champs. Si un objet sérialisé ne comporte pas tous les champs obligatoires d’un modèle, la désérialisation ne sera pas capable d’enregistrer les instances désérialisées.

### Modèles hérités

Si un modèle est défini par une classe de base abstraite, il n’y a rien de particulier à faire pour pouvoir sérialiser ce modèle. Appelez le sérialiseur sur l’objet (ou les objets) que vous voulez sérialiser et le résultat sera une représentation complète de l’objet sérialisé.

Cependant, si un modèle utilise de l’héritage multi-table, il sera alors aussi nécessaire de sérialiser toutes les classes de base du modèle. C’est parce que seuls les champs définis localement dans le modèle seront sérialisés. Par exemple, considérons les modèles suivants :

    class Place(models.Model):
        name = models.CharField(max_length=50)

    class Restaurant(Place):
        serves_hot_dogs = models.BooleanField(default=False)

Si vous ne sérialisez que le modèle `Restaurant`:

    data = serializers.serialize("json", Restaurant.objects.all())

les champs de la sortie sérialisée ne contiendront que l’attribut `serves_hot_dogs`. L’attribut `name` de la classe de base sera ignoré.

Afin de sérialiser complètement les instances de `Restaurant`, il sera aussi nécessaire de sérialiser les modèles `Place`:

    all_objects = [*Restaurant.objects.all(), *Place.objects.all()]
    data = serializers.serialize("json", all_objects)

## Désérialisation de données

La désérialisation de données est très semblable à la sérialisation :

    for obj in serializers.deserialize("json", data):
        do_something_with(obj)

Comme vous pouvez le voir, la fonction `deserialize` accepte le même paramètre `format` que `serialize`, une chaîne ou un flux de données, et renvoie un itérateur.

Cependant, ça se complique un peu à cet endroit. Les objets renvoyés par l’itérateur `deserialize` ne sont pas des objets Django normaux. Il s’agit d’instances `DeserializedObject` spéciales qui englobent un objet crée mais non enregistré ainsi que les données de liaison associées.

L’appel à `DeserializedObject.save()` enregistre l’objet dans la base de données.

Note

Si l’attribut `pk` n’existe pas dans les données sérialisées ou vaut `null`, une nouvelle instance est enregistrée dans la base de données.

Cela garantit que la désérialisation est une opération non destructive, même si les données de la représentation sérialisée ne correspondent pas à ce qui se trouve actuellement dans le base de données. Généralement, voici comment l’on traite ces instances de `DeserializedObject`:

    for deserialized_object in serializers.deserialize("json", data):
        if object_should_be_saved(deserialized_object):
            deserialized_object.save()

En d’autres termes, l’utilisation habituelle est d’examiner les objets désérialisés pour s’assurer qu’ils sont « cohérents » avant de les enregistrer. Si vous avez confiance dans votre source de données, vous pouvez aussi enregistrer directement l’objet et passer à la suite.

L’objet Django lui-même peut être inspecté avec `deserialized_object.object`. Si des champs dans les données sérialisées n’existent pas dans un modèle, une exception `DeserializationError` est générée, sauf si le paramètre `ignorenonexistent` a été transmis avec la valeur `True`:

    serializers.deserialize("json", data, ignorenonexistent=True)

## Formats de sérialisation

Django prend en charge quelques formats de sérialisation, certains dépendant de l’installation de modules Python supplémentaires :

Identifiant | Information
--- | ---
`xml` | Sérialise vers et à partir d’un dialecte XML simple.
`json` | Sérialise vers et à partir du format JSON.
`jsonl` | Sérialise vers et à partir du format JSONL.
`yaml` | Sérialise en YAML (YAML Ain’t a Markup Language). Ce sérialiseur n’est disponible que si PyYAML est installé.

### XML

Le format basique de sérialisation XML ressemble à ceci :

    <?xml version="1.0" encoding="utf-8"?>
    <django-objects version="1.0">
        <object pk="123" model="sessions.session">
            <field type="DateTimeField" name="expire_date">2013-01-16T08:16:59.844560+00:00</field>
            <!-- ... -->
        </object>
    </django-objects>

La collection complète des objets sérialisés ou désérialisés est représentée par une balise `<django-objects>` contenant plusieurs éléments `<object>`. Chacun de ces objets contient deux attributs : « pk » et « model », ce dernier étant constitué du nom de l’application (« sessions ») et du nom du modèle en minuscules (« session »), séparés par un point.

Each field of the object is serialized as a `<field>`-element sporting the fields « type » and « name ». The text content of the element represents the value that should be stored. (If the element contains child tags, `SuspiciousOperation` is raised.)

Les clés étrangères et les autres champs relationnels sont traités légèrement différemment :

    <object pk="27" model="auth.permission">
        <!-- ... -->
        <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">9</field>
        <!-- ... -->
    </object>

Dans cet exemple, l’objet `auth.Permission` ayant la clé primaire 27 possède une clé étrangère vers l’instance `contenttypes.ContentType` ayant la clé primaire 9.

Les relations plusieurs-à-plusieurs sont exportées pour le modèle qui fait office de lien. Par exemple, le modèle `auth.User` possède une telle relation vers le modèle `auth.Permission`:

    <object pk="1" model="auth.user">
        <!-- ... -->
        <field to="auth.permission" name="user_permissions" rel="ManyToManyRel">
            <object pk="46"></object>
            <object pk="47"></object>
        </field>
    </object>

Cet exemple lie l’utilisateur donné avec les modèles de permission ayant les clés primaires 46 et 47.

Caractères de contrôle

Si le contenu à sérialiser contient des caractères de contrôle qui ne sont pas acceptés par le standard XML 1.0, la sérialisation échoue avec une exception `ValueError`. Lisez également l’explication du W3C dans le document HTML, XHTML, XML and Control Codes.

Changed in Django 6.1:

`SuspiciousOperation` is raised when unexpected nested tags are found.

### JSON

Tout en conservant les mêmes données d’exemple que précédemment, voici ce que donnerait leur sérialisation au format JSON :

    [
        {
            "pk": "4b678b301dfd8a4e0dad910de3ae245b",
            "model": "sessions.session",
            "fields": {
                "expire_date": "2013-01-16T08:16:59.844Z",
                # ...
            },
        }
    ]

La mise en forme est ici un peu plus simple qu’en XML. L’ensemble de la collection est représentée par un seul tableau et les objets sont représentés par des objets JSON ayant trois propriétés : « pk », « model » et « fields ». « fields » est lui-même un objet contenant les noms et valeurs de chaque champ respectivement comme propriété et propriété-valeur.

Les clés étrangères ont la clé primaire de l’objet lié comme valeur de propriété. Les objets des relations plusieurs-à-plusieurs sont sérialisés avec le modèle qui les définit et sont représentés sous forme de liste de clés primaires.

Il faut savoir que certains contenus Django ne peuvent pas être transmis tels quels à `json`. Par exemple, si l’un de vos objets à sérialiser contient un type personnalisé, vous devrez écrire un codeur `json` dédié. Quelque chose comme ceci devrait fonctionner :

    from django.core.serializers.json import DjangoJSONEncoder

    class LazyEncoder(DjangoJSONEncoder):
        def default(self, obj):
            if isinstance(obj, YourCustomType):
                return str(obj)
            return super().default(obj)

Vous pouvez alors passer `cls=LazyEncoder` à la fonction `serializers.serialize()`:

    from django.core.serializers import serialize

    serialize("json", SomeModel.objects.all(), cls=LazyEncoder)

Notez également que GeoDjango met à disposition un sérialiseur adapté à GeoJSON.

#### `DjangoJSONEncoder`

class django.core.serializers.json.DjangoJSONEncoder

Le sérialiseur JSON utilise `DjangoJSONEncoder` pour le codage. C’est une sous-classe de `JSONEncoder`, qui ajoute le codage de types supplémentaires :

`datetime`

  Une chaîne sous la forme `AAAA-MM-JJTHH:mm:ss.sssZ` ou `AAAA-MM-JJTHH:mm:ss.sss+HH:MM` telle que définie dans ECMA-262.

`date`

  Une chaîne sous la forme `AAAA-MM-JJ` telle que définie dans ECMA-262.

`time`

  Une chaîne sous la forme `HH:MM:ss.sss` telle que définie dans ECMA-262.

`timedelta`

  Une chaîne représentant une durée telle que définie par ISO-8601. Par exemple, `timedelta(days=1, hours=2, seconds=3.4)` est représenté par `'P1DT02H00M03.400000S'`.

`Decimal`, `Promise` (objets `django.utils.functional.lazy()`), `UUID`

  Une représentation textuelle de l’objet.

### JSONL

JSONL signifie JSON Lines. Avec ce format, les objets sont séparés par des sauts de lignes et chaque ligne contient un objet JSON valide. Les données sérialisées en JSONL ressemblent à ceci

    {"pk": "4b678b301dfd8a4e0dad910de3ae245b", "model": "sessions.session", "fields": {...}}
    {"pk": "88bea72c02274f3c9bf1cb2bb8cee4fc", "model": "sessions.session", "fields": {...}}
    {"pk": "9cf0e26691b64147a67e2a9f06ad7a53", "model": "sessions.session", "fields": {...}}

JSONL peut se révéler utile pour remplir de grosses bases de données, dans la mesure où les données peuvent être traitées ligne par ligne, plutôt que d’être toutes chargées intégralement en mémoire.

### YAML

La sérialisation YAML ressemble beaucoup à celle de JSON. La liste d’objets est sérialisée comme une suite de correspondances avec les clés « pk », « model » et « fields ». Chaque champ est lui-même une correspondance entre la clé contenant le nom du champ et la valeur contenant la valeur du champ :

    - model: sessions.session
      pk: 4b678b301dfd8a4e0dad910de3ae245b
      fields:
        expire_date: 2013-01-16 08:16:59.844560+00:00

Les champs de référence sont également représentés par la clé primaire ou une suite de clés primaires.

### Formats de sérialisation personnalisés

En plus des formats par défaut, vous pouvez créer des formats de sérialisation personnalisés.

Par exemple, imaginons un sérialiseur et désérialiseur csv. Tout d’abord, définissez des classes `Serializer` et `Deserializer`. Celles-ci peuvent surcharger des classes existantes de formats de sérialisation :

`chemin/vers/serialiseur_csv_personnalise.py`

     import csv

     from django.apps import apps
     from django.core import serializers
     from django.core.serializers.base import DeserializationError

     class Serializer(serializers.python.Serializer):
         def get_dump_object(self, obj):
             dumped_object = super().get_dump_object(obj)
             row = [dumped_object["model"], str(dumped_object["pk"])]
             row += [str(value) for value in dumped_object["fields"].values()]
             return ",".join(row), dumped_object["model"]

         def end_object(self, obj):
             dumped_object_str, model = self.get_dump_object(obj)
             if self.first:
                 fields = [field.name for field in apps.get_model(model)._meta.fields]
                 header = ",".join(fields)
                 self.stream.write(f"model,{header}\n")
             self.stream.write(f"{dumped_object_str}\n")

         def getvalue(self):
             return super(serializers.python.Serializer, self).getvalue()

     class Deserializer(serializers.python.Deserializer):
         def __init__(self, stream_or_string, **options):
             if isinstance(stream_or_string, bytes):
                 stream_or_string = stream_or_string.decode()
             if isinstance(stream_or_string, str):
                 stream_or_string = stream_or_string.splitlines()
             try:
                 objects = csv.DictReader(stream_or_string)
             except Exception as exc:
                 raise DeserializationError() from exc
             super().__init__(objects, **options)

         def _handle_object(self, obj):
             try:
                 model_fields = apps.get_model(obj["model"])._meta.fields
                 obj["fields"] = {
                     field.name: obj[field.name]
                     for field in model_fields
                     if field.name in obj
                 }
                 yield from super()._handle_object(obj)
             except (GeneratorExit, DeserializationError):
                 raise
             except Exception as exc:
                 raise DeserializationError(f"Error deserializing object: {exc}") from exc

Puis, ajoutez le module contenant les définitions de sérialisation dans votre réglage `SERIALIZATION_MODULES`:

    SERIALIZATION_MODULES = {
        "csv": "path.to.custom_csv_serializer",
        "json": "django.core.serializers.json",
    }

## Clés naturelles

La stratégie de sérialisation par défaut pour les clés étrangères et les relations plusieurs-à-plusieurs est de sérialiser la valeur des clés primaires des objets de la relation. Cette stratégie fonctionne bien pour la plupart des objets, mais elle peut provoquer des difficultés en certaines circonstances.

Considérons le cas d’une liste d’objets ayant une clé étrangère référençant `ContentType`. Si vous sérialisez un objet qui se réfère à un type de contenu, il faut d’abord pouvoir trouver un moyen de faire référence à ce type de contenu. Comme les objets `ContentType` sont créés automatiquement par Django durant le processus de synchronisation de la base de données, la clé primaire d’un type de contenu n’est pas facile à prédire ; cela dépend de la manière et du moment où `migrate` a été exécuté. C’est pareil pour tous les modèles qui génèrent automatiquement des objets, comme par exemple `Permission`, `Group` et `User`.

Avertissement

Vous ne devriez jamais inclure des objets générés automatiquement dans des instantanés ou d’autres données sérialisées. Il est possible qu’avec un peu de chance, les clés primaires de l’instantané correspondent à celles de la base de données et que le chargement de l’instantané passe sans autre. Mais dans le cas plus probable où elles ne correspondent pas, le chargement de l’instantané échouera avec une exception `IntegrityError`.

Il y a aussi des raisons plus pratiques. Un identifiant nombre entier n’est pas toujours la manière la plus intuitive de se référer à un objet ; parfois, il est bien utile de disposer d’une référence plus naturelle.

C’est pour ces raisons que Django fournit les clés naturelles. Une clé naturelle est un tuple de valeurs servant à identifier de manière unique une instance d’objet sans avoir besoin de la valeur de clé primaire.

### Désérialisation des clés naturelles

Considérons les deux modèles suivants :

    from django.db import models

    class Person(models.Model):
        first_name = models.CharField(max_length=100)
        last_name = models.CharField(max_length=100)

        birthdate = models.DateField()

        class Meta:
            constraints = [
                models.UniqueConstraint(
                    fields=["first_name", "last_name"],
                    name="unique_first_last_name",
                ),
            ]

    class Book(models.Model):
        name = models.CharField(max_length=100)
        author = models.ForeignKey(Person, on_delete=models.CASCADE)

En principe, les données sérialisées de `Book` utilisent un nombre entier pour se référer à l’auteur. Par exemple, en JSON, un objet `Book` pourrait être sérialisé comme :

    ...
    {"pk": 1, "model": "store.book", "fields": {"name": "Mostly Harmless", "author": 42}}
    ...

Cette manière de se référer à un auteur n’est pas particulièrement intuitive. Il est nécessaire de connaître la valeur de clé primaire de l’auteur concerné ; il faut aussi que la clé primaire soit stable et prédictible.

Cependant, si nous ajoutons la capacité de clé naturelle à `Person`, l’instantané prend tout de suite un air plus humain. Pour ajouter la capacité de clé naturelle, il s’agit de définir un gestionnaire par défaut pour `Person` avec une méthode `get_by_natural_key()`. Dans le cas de `Person`, on pourrait utiliser le prénom et le nom comme bonne clé naturelle :

    from django.db import models

    class PersonManager(models.Manager):
        def get_by_natural_key(self, first_name, last_name):
            return self.get(first_name=first_name, last_name=last_name)

    class Person(models.Model):
        first_name = models.CharField(max_length=100)
        last_name = models.CharField(max_length=100)
        birthdate = models.DateField()

        objects = PersonManager()

        class Meta:
            constraints = [
                models.UniqueConstraint(
                    fields=["first_name", "last_name"],
                    name="unique_first_last_name",
                ),
            ]

Les livres peuvent maintenant utiliser la clé naturelle pour faire référence aux objets `Person`:

    ...
    {
        "pk": 1,
        "model": "store.book",
        "fields": {"name": "Mostly Harmless", "author": ["Douglas", "Adams"]},
    }
    ...

Au moment de charger ces données sérialisées, Django utilise la méthode `get_by_natural_key()` pour obtenir la clé primaire d’un objet `Person` existant à partir des informations `["Douglas", "Adams"]`.

Note

Quels que soient les champs utilisés pour définir une clé primaire, ils doivent pouvoir identifier un objet de manière unique. Cela signifie généralement que le modèle possède une clause d’unicité (soit `unique=True` pour un seul champ ou `UniqueConstraint` ou `unique_together` pour plusieurs champs) pour le ou les champs composant la clé naturelle. Cependant, l’unicité ne doit pas nécessairement être garantie au niveau de la base de données. Si vous êtes sûr qu’un certain ensemble de champs sera effectivement unique, vous pouvez très bien utiliser ces champs comme clé naturelle.

La désérialisation d’objets sans clé primaire vérifie toujours si le gestionnaire du modèle possède une méthode `get_by_natural_key()` et l’utilise le cas échéant pour compléter la clé primaire des objets désérialisés.

### Sérialisation des clés naturelles

Comment donc pousser Django à émettre une clé naturelle lorsqu’il sérialise un objet ? Premièrement, vous devez ajouter une autre méthode, cette fois dans le modèle lui-même :

    class Person(models.Model):
        first_name = models.CharField(max_length=100)
        last_name = models.CharField(max_length=100)
        birthdate = models.DateField()

        objects = PersonManager()

        class Meta:
            constraints = [
                models.UniqueConstraint(
                    fields=["first_name", "last_name"],
                    name="unique_first_last_name",
                ),
            ]

        def natural_key(self):
            return (self.first_name, self.last_name)

Cette méthode doit toujours renvoyer un tuple de clés naturelles ; dans cet exemple, il s’agit de `(first name, last name)`. Puis, lors de l’appel à `serializers.serialize()`, il faut indiquer les paramètres `use_natural_foreign_keys=True` ou `use_natural_primary_keys=True`:

    >>> serializers.serialize(
    ...     "json",
    ...     [book1, book2],
    ...     indent=2,
    ...     use_natural_foreign_keys=True,
    ...     use_natural_primary_keys=True,
    ... )

Lorsque `use_natural_foreign_keys=True` est indiqué, Django utilise la méthode `natural_key()` pour sérialiser toute référence de clé étrangère à des objets d’un type définissant cette méthode.

Lorsque `use_natural_primary_keys=True` est indiqué, Django ne produit pas de clé primaire dans les données sérialisées de cet objet dans la mesure où elle peut être calculée durant la désérialisation :

    ...
    {
        "model": "store.person",
        "fields": {
            "first_name": "Douglas",
            "last_name": "Adams",
            "birth_date": "1952-03-11",
        },
    }
    ...

Cela peut être utile lorsqu’il y a besoin de charger des données sérialisées dans une base de données existante et qu’il n’est pas possible de garantir que les valeurs de clés primaires sérialisées ne sont pas déjà utilisées, et qu’il n’est pas nécessaire de conserver les clés primaires exactes des objets désérialisés.

Si vous utilisez `dumpdata` pour générer des données sérialisées, utilisez les options de ligne de commande `dumpdata --natural-foreign` et `dumpdata --natural-primary` pour générer des clés naturelles.

Note

Il n’est pas obligatoire de définir à la fois `natural_key()` et `get_by_natural_key()`. Si vous ne souhaitez pas que Django produise des clés naturelles pendant la sérialisation mais que vous voulez conserver la possibilité de charger des clés naturelles, vous pouvez choisir de n’implémenter que la méthode `natural_key()`.

Inversement, si (pour une raison étrange) vous voulez que Django produise des clés naturelles pendant la sérialisation, mais qu’il ne puisse pas charger ces valeurs, il suffit d’implémenter la méthode `get_by_natural_key()`.

Subclasses can opt out of natural key serialization by returning an empty tuple (`()`) from `natural_key()`. This tells the serializer to fall back to the standard primary key.

Changed in Django 6.1:

Support for opting out of natural key serialization by returning an empty tuple was added.

### Clés naturelles et références en aval

Parfois, lors de l’utilisation de clés étrangères naturelles, il est nécessaire de désérialiser des données d’un objet possédant une clé étrangère se référant à un autre objet pas encore sérialisé. On appelle cela une « référence en aval ».

Par exemple, supposons que votre instantané contiennent les objets suivants

    ...
    {
        "model": "store.book",
        "fields": {"name": "Mostly Harmless", "author": ["Douglas", "Adams"]},
    },
    ...
    {"model": "store.person", "fields": {"first_name": "Douglas", "last_name": "Adams"}},
    ...

Afin de pouvoir gérer cette situation, il faut passer `handle_forward_references=True` à `serializers.deserialize()`. Cela marquera l’attribut `deferred_fields` sur les instances `DeserializedObject`. Vous devez ensuite garder la trace des instances `DeserializedObject` où cet attribute n’est pas `None` et appeler plus tard `save_deferred_fields()` sur ces instances.

Voici à quoi peut ressembler une utilisation typique :

    objs_with_deferred_fields = []

    for obj in serializers.deserialize("json", data, handle_forward_references=True):
        obj.save()
        if obj.deferred_fields is not None:
            objs_with_deferred_fields.append(obj)

    for obj in objs_with_deferred_fields:
        obj.save_deferred_fields()

Pour que ça fonctionne, la clé `ForeignKey` du modèle qui y fait référence doit avoir `null=True`.

### Dépendances durant la sérialisation

Il est souvent possible d’éviter de devoir gérer explicitement les références en aval en prenant soin de placer les objets d’un instantané dans le bon ordre.

Pour aider à faire cela, les appels à `dumpdata` utilisant l’option `dumpdata --natural-foreign` sérialisent en premier les modèles possédant une méthode `natural_key()`, avant de sérialiser les objets à l’aide de leur clé primaire standard.

Cependant, ce n’est pas toujours suffisant. Si une clé naturelle se réfère à un autre objet (en utilisant une clé étrangère ou une clé naturelle vers un autre objet faisant partie d’une clé naturelle), il faut être capable de garantir que les objets dont dépend une clé naturelle apparaissent dans les données sérialisées avant la clé naturelle qui en a besoin.

Pour contrôler cet ordre d’apparition, il est possible de définir des dépendances dans les méthodes `natural_key()`. Cela se fait en définissant un attribut `dependencies` sur la méthode `natural_key()` elle-même.

Par exemple, ajoutons une clé naturelle au modèle `Book` à partir de l’exemple ci-dessus :

    class Book(models.Model):
        name = models.CharField(max_length=100)
        author = models.ForeignKey(Person, on_delete=models.CASCADE)

        def natural_key(self):
            return (self.name,) + self.author.natural_key()

La clé naturelle d’un objet `Book` est une combinaison de son nom et de son auteur. Cela signifie que `Person` doit être sérialisé avant `Book`. Pour définir cette dépendance, nous ajoutons une ligne supplémentaire :

    def natural_key(self):
        return (self.name,) + self.author.natural_key()

    natural_key.dependencies = ["example_app.person"]

Cette définition garantit que tous les objets `Person` sont sérialisés avant les objets `Book`. Sur le même principe, tout objet référençant `Book` sera sérialisé après les objets `Person` et `Book`.