Aller au contenu

Répondre à

Vous pouvez utiliser le modèle ormar au lieu du modèle pydantique dans le modèle fastapi response_.

Si vous le souhaitez, vous pouvez bien sûr mélanger Ormar.Models avec Pydantic.

Une des tâches les plus courantes dans une réponse consiste à exclure certains champs que vous ne voulez pas inclure dans vos données de réponse.

Cela peut être fait de plusieurs façons dans Ormar, vous pouvez donc consulter vos options ci-dessous et choisir celle qui convient le mieux à votre situation.

Exclure les champs dans la réponse

Champs optionnels

Notez que chaque champ facultatif n'est pas obligatoire, ce qui signifie que les champs facultatifs peuvent être ignorés à la fois dans la réponse et la demande.

Les champs ne sont pas nécessaires si (tous/plusieurs/tous):

  • Le champ est marqué nullable=True
  • Le champ a une valeur par défaut ou une fonction fournie, c'est-à-dire default="Test"
  • Le champ a la valeur server_default définie
  • Field est un champ autoincrement=Trueprimary_key (notez que ormar.Integerprimary_key est autoincrement par défaut)

Exemples:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
base_ormar_config = ormar.OrmarConfig(
    Metadata= métadonnée
    database=database
)

Classe Utilisateur (modèle ormar):
    ormar_config = base_ormar_config.copy()

    id: int = ormar.Integer(primary_key=True)
    email: str = ormar.String(max_length=255)
    password: str = ormar.String(max_length=255)
    first_name: str = ormar.String(max_length=255, nullable=True)
    last_name: str = ormar.String(max_length=255)
    catégorie: str = ormar.String(max_length=255, default="User")

Dans l'exemple ci-dessus, le champ id (qui est un entier incrémental automatique), le premier_nom (avec nullable=True) et la catégorie (avec valeur par défaut) sont facultatifs et peuvent être ignorés dans la réponse, et le modèle sera toujours validé.

Si le champ peut être vide, il n'est pas nécessaire de l'inclure dans la charge utile lors de sa création ainsi que dans la réponse, donc selon l'exemple ci-dessus, vous pouvez:

1
2
3
4
# note cette application est une application FastApi
@ app.post ("/users/", response_model=User) #ici nous utilisons l'ormar.Model in response
async def create_user(user: User): #ici nous utilisons ormar. modèle dans le paramètre de demande
    return await user.save ()

Cela signifie que si vous ne passez pas ie first_name dans votre demande, il sera correctement validé (car le champ est facultatif), enregistré dans la base de données et retourné l'enregistrement enregistré sans ce champ (qui sera également validé).

Note!!!Notez que même si vous ne passez pas la valeur du champ, le champ lui-même existe toujours dans le modèle de réponse, ce qui signifie qu 'il apparaîtra dans les données de réponse et sera défini sur None.

1
Si vous souhaitez exclure complètement le champ du résultat lu on.

FastApi Response_Model_Exclusion

Fastapi possède une réponse_modèle_exclude qui accepte un ensemble (ou une liste) de noms de champs.

Cela a ses limites, car ormar et pydantic acceptent également les dictionnaires dans lesquels vous pouvez définir des colonnes d'exclusion/inclusion qui peuvent également être sur des modèles imbriqués (voir ci-dessous pour plus d'informations).

Attention: un champ obligatoire ne peut pas être exclu lors de l'utilisation de response_model car il échouera lors de la validation.

1
2
3
@ app.post ("/users/", response_model=User, response_model_exclude={"password"})
async def create_user(user: User):
    return await user.save ()

Le point final ci-dessus peut être interrogé comme ceci:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
depuis starlette.testclient import TestClient

client = TestClient(app)

Avec un client comme client:
        # note il n'y a pas de pk
        utilisateur =
            "email": "test@domain.com",
            "password": "^*^%A*DA*IAAA",
            "first_name": "John",
            "last_name": "Doe",
        }
        response = client.post ("/users/", json=user)
        # note que le champ exclu est complètement gagné par la réponse
        assert "password" not in response.json()
        #lire la réponse et initialiser le modèle hors de lui
        created_user = User(**response.json()))
        # note pk est populé par autoincrement
        assert created_user.pk n'est pas None
        # note que le mot de passe est manquant dans le modèle initialisé aussi
        assert created_user.password est None

!!! Remarquez comment le champ mot de passe dans l'exemple ci-dessus disparaît complètement des données de réponse.

1
Note que vous pouvez utiliser cette méthode uniquement pour les champs non requis.

Exclusion du modèle imbriqué

Bien que fastapi ne permette qu 'un seul ensemble de noms de champs à passer, et donc simplement à exclure, ormar est plus intelligent lorsqu' il s'agit d'utiliser response_model_exclude.

Dans ormar, vous pouvez exclure des modèles imbriqués en utilisant deux types de symboles.

Le premier est un dictionnaire avec des champs imbriqués qui représentent la structure de l'arbre du modèle, et le second est un chemin de nom de champ séparé par un double soulignement.

Supposons que notre catégorie d'utilisateurs soit un modèle distinct:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
base_ormar_config = ormar.OrmarConfig(
    Metadata= métadonnée
    database=database
)

Catégorie de classe (modèle ormar):
    ormar_config = base_ormar_config.copy(tablename="catégories")

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=255)    
    priorité: int = ormar.Integer(nullable=True)


Classe Utilisateur (modèle ormar):
    ormar_config = base_ormar_config.copy()

    id: int = ormar.Integer(primary_key=True)
    email: str = ormar.String(max_length=255)
    password: str = ormar.String(max_length=255)
    first_name: str = ormar.String(max_length=255, nullable=True)
    last_name: str = ormar.String(max_length=255)
    Category: Optional[Category] = ormar.ForeignKey(Category, related_name="categories")

Si vous souhaitez exclure la priorité d'une catégorie dans une réponse, vous pouvez toujours utiliser le paramètre fastapi.

1
2
3
@ app.post ("/users/", response_model=User, response_model_exclude={"category__priority"})
async def create_user(user: User):
    return await user.save ()

Notez que vous pouvez utiliser le double soulignement pour accéder à des modèles plus profonds, et si vous souhaitez exclure plusieurs champs du modèle imbriqué, vous devez les précéder du chemin complet.response_model_exclude={"category__priority", "category__other_field", category__nested_model__nested_model_field} ETC dans l'exemple.

!!!Remarque Pour en savoir plus sur les exclusions possibles et sur la façon de construire un dictionnaire ou un paramètre d'exclusion, consultez la section Champs de la documentation.

Notez qu 'en plus du paramètre response_model_exclude, fastapi prend en charge d'autres paramètres hérités de pydantic. Tout cela s'applique également à ormar, mais peut avoir quelques nuances, il est donc préférable de lire la section dict de la documentation.

Exclure dans Model.model_dump()

Alternativement, vous pouvez simplement retourner un dictionnaire à partir d'ormar.Model et l'utiliser.

De cette façon, vous pouvez également définir l'exclusion/inclusion comme dictionnaire et exclure les champs du modèle imbriqué.

!!!Avertissement Ne pas utiliser response_model entraînerait un document api sans exemples et schémas de réponse, car les réponses peuvent théoriquement être de n'importe quel format.

1
2
3
4
5
@ app.post ("/users2/", response_model= utilisateur)
async def create_user2(user: User):
    user = await user.save ()
    return user.model_dump(exclude={'password'})
    #pourrait être aussi quelque chose comme retourner l'utilisateur. modèle_dump(exclude="category":{'priority'}}) pour exclure la priorité de la catégorie

!!!Remarque Notez que même si vous passez un champ de mot de passe dans une requête, l'exemple ci-dessus invalide le champ de mot de passe, mais le champ existera toujours car il fait partie du schéma de réponse et la valeur sera définie sur None.

Si vous souhaitez exclure complètement le champ de cette façon, il suffit de ne pas utiliser response_model et d'exclure dans model_dump() du modèle.

Alternativement, vous pouvez simplement renvoyer un dictionnaire à partir du modèle ormar. comme cela, vous pouvez également définir l'exclusion/inclusion comme dictionnaire et exclure les champs du modèle imbriqué.

!!!Remarque Théoriquement, vous perdez la validation de la réponse ici, mais comme vous travaillez sur ormar.Models, les données de réponse sont déjà validées après la requête de la base de données (car le modèle ormar est un modèle pydantique).

Donc, si vous ignorez complètement response_model, vous pouvez faire ce qui suit:

1
2
3
4
@ app.post ("/users4/") # note no response_model
async def create_user4(user: User):
    user = await user.save ()
    return user.model_dump(exclude={'last_name'})

!!!Remarque Notez que lorsque vous ignorez le modèle de réponse_, vous pouvez également exclure les champs obligatoires, car la réponse n'est plus validée après le retour.

1
Le coût de cette solution est que vous perdez aussi api documentation comme schéma de réponse dans inconnu de la perspective fastapi.

Générer un modèle pydantique à partir d'un modèle ormar.

Étant donné que la tâche d'exclure des champs est très courante, ormar dispose d'une méthode spéciale pour générer des modèles pydantiques à partir d'un modèle ormar.Models existant sans avoir à ressaisir tous les champs.

Cette méthode est la méthode get_pydantic() disponible pour toutes les classes de modèle.

1
2
3
4
5
# generate a tree of models without password on User et without priority on nested Category
ResponseUser = User.get_pydantic(exclude={"password": ..., "category":{"priority"}})
@ app.post ("/users3/", response_model=ResponseUser) # use the generated model here
async def create_user3(user: User):
    return await user.save ()

!!! NOTE Pour voir plus d'exemples et lire plus, visitez la section get_pydantic de la documentation.

Attention La méthode get_pydantic génère tous les modèles de l'arbre de modèles imbriqués selon un algorithme qui permet d'éviter les boucles dans le modèle (même algorithme utilisé dans model_dump(), select_all(), etc.).

1
2
3
Cela signifie que les modèles naturels n'ont pas de référence au modèle parent (par défaut, la relation orale est bidirectionnelle).

Remarque: si le modèle donné existe dans un arbre plus qu 'une fois, il sera doublé dans les modèles pydantiques (chaque occurrence aura son propre modèle séparé).

Modèles cantilever indépendants

La solution finale consiste à créer manuellement des modèles pydantiques distincts. cela fonctionne exactement comme une application fastapi normale, vous pouvez donc utiliser différents modèles de réponse et de demande, etc.

Sample de:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Import Pydantic

Classe UserBase(pydantic.BaseModel):
    model_config = pydantic.ConfigDict(from_attributes=True)

    Email: str
    premier_nom: str
    last_name: str


@ app.post ("/users3/", response_model=UserBase) # use pydantic model ici
async def create_user3(user: User): #use omar model here (mais de course vous pouvez utiliser pydantic aussi ici)
    return await user.save ()