3 votes

Modèle pydantique avec champ Union avec une option marquer comme obsolète

J'ai quelques modèles Pydantic avec des champs qui sont des unions de différents modèles. Je cherche un moyen de rendre certains modèles de l'union obsolètes.

Je peux rendre l'ensemble du champ obsolète avec : Field(default=None, deprecated=True)

mais je ne trouve aucun moyen de le faire sur l'une des valeurs possibles. Par exemple, dans l'exemple suivant, est-il possible de marquer SimpleUser comme deprecated pour le modèle Log et de générer la documentation en conséquence.

from typing import Union

from pydantic import BaseModel, Field

class Admin(BaseModel):
    name: str

class SimpleUser(BaseModel):
    age: int

class Log(BaseModel):
    user: Union[Admin, SimpleUser, None] = Field(default=None)

1voto

Daniil Fajnberg Points 580

Pydantic crée des schémas de modèles imbriqués à l'aide de $ref dans les propriétés définies dans d'autres modèles. Le schéma aura alors, par défaut, un fichier definitions contenant les schémas qui y sont référencés.

Dans votre cas particulier, le Log ressemblerait à ceci :

{
  "title": "Log",
  "type": "object",
  "properties": {
    "user": {
      "title": "User",
      "anyOf": [
        {
          "$ref": "#/definitions/Admin"
        },
        {
          "$ref": "#/definitions/SimpleUser"
        }
      ]
    }
  },
  "definitions": {
    "Admin": {
      "title": "Admin",
      "type": "object",
      "properties": {
        "name": {
          "title": "Name",
          "type": "string"
        }
      },
      "required": [
        "name"
      ]
    },
    "SimpleUser": {
      "title": "SimpleUser",
      "type": "object",
      "properties": {
        "age": {
          "title": "Age",
          "type": "integer"
        }
      },
      "required": [
        "age"
      ]
    }
  }
}

L'union se reflète dans la anyOf chaque objet du tableau faisant référence à l'un des modèles.

D'après ce que j'ai compris de la spécification actuelle du schéma JSON, vous êtes autorisé à ajouter d'autres mots clés aux côtés de $ref . Je suppose donc que vous pouvez communiquer que SimpleUser est une option obsolète pour user en ajoutant simplement l'élément deprecated à côté de la référence au SimpleUser définition.

Pydantic vous permet de contrôler la façon dont le schéma JSON est construit grâce à l'option schema_extra paramètre de configuration. Si vous le définissez comme une méthode statique de la méthode interne Config vous pouvez la modifier à votre guise.

La mise en œuvre de cet ajout pourrait donc se faire de la manière suivante :

from typing import Any

from pydantic import BaseModel, Field

class Admin(BaseModel):
    name: str

class SimpleUser(BaseModel):
    age: int

class Log(BaseModel):
    user: Admin | SimpleUser | None = Field(default=None)

    class Config:
        @staticmethod
        def schema_extra(schema: dict[str, Any]) -> None:
            """Marks the `SimpleUser` option for `user` as deprecated."""
            user_property = schema["properties"]["user"]
            for obj in user_property["anyOf"]:
                ref = obj.get("$ref")
                if isinstance(ref, str) and ref.endswith("/SimpleUser"):
                    obj["deprecated"] = True
                    break

La sortie de print(Log.schema_json(indent=2)) maintenant :

{
  "title": "Log",
  "type": "object",
  "properties": {
    "user": {
      "title": "User",
      "anyOf": [
        {
          "$ref": "#/definitions/Admin"
        },
        {
          "$ref": "#/definitions/SimpleUser",
          "deprecated": true
        }
      ]
    }
  },
  "definitions": {
    "Admin": {
      "title": "Admin",
      "type": "object",
      "properties": {
        "name": {
          "title": "Name",
          "type": "string"
        }
      },
      "required": [
        "name"
      ]
    },
    "SimpleUser": {
      "title": "SimpleUser",
      "type": "object",
      "properties": {
        "age": {
          "title": "Age",
          "type": "integer"
        }
      },
      "required": [
        "age"
      ]
    }
  }
}

La seule différence par rapport à la précédente est l'ajout de "deprecated": true .

Je ne suis pas sûr à 100 % que ce schéma soit compris par les clients de la façon dont vous l'entendez, car je n'ai trouvé aucun exemple d'utilisation de cette façon, mais je n'ai pas non plus trouvé d'indication contraire et je pense que ce schéma est conforme à la spécification. Peut-être que quelqu'un ayant plus d'expérience avec JSON Schema peut commenter ceci pour (dis-)confirmer.


PS :

Si les propriétés additionnelles à côté de la $ref sont ignorées par le client parce qu'il s'en tient à l'option OpenAPI 3.1 vous pouvez toujours définir la propriété dépréciée sur l'ensemble de l'élément SimpleUser modèle :

from pydantic import BaseModel, Field

class Admin(BaseModel):
    name: str

class SimpleUser(BaseModel):
    age: int

    class Config:
        schema_extra = {"deprecated": True}

class Log(BaseModel):
    user: Admin | SimpleUser | None = Field(default=None)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X