• contact@spgoo.org

FastAPI — beanie — MongoDB

Description du couplage entre le service web et la base de données :

Lien vers la documentation de beanie :

Développement du module de database avec les interfaces dédiées pour les différentes opérations souhaitées de manipulation des données dans le cadre du projet de rétrosynthèse. dans ce projet on souhaite stocker dans un système MongoDB deux types de données, les informations sur la rétrosynthèse (le smile, date, la référence interne et nb_routes) et les commentaires sur chaque solution trouvée (date, texte du commentaire). Ces différentes informations seront stockées dans une collection dédiée : retrosyntheses et comments. Donc les opérations à réaliser couvrent les aspects classiques d’un CRUD.

Exemple de module database : CRUD

from beanie import init_beanie, PydanticObjectId
from beanie.operators import In, RegEx
from motor.motor_asyncio import AsyncIOMotorClient
from pydantic import BaseModel
from pydantic_settings import BaseSettings
from typing import Any, List, Optional
from models.retrosynthese import Retrosynthese
from models.comments import CommentDoc

class Settings(BaseSettings):
    DATABASE_URL: Optional[str] = None
    SECRET_KEY: Optional[str] = None
    async def initialize_database(self):
        client = AsyncIOMotorClient(self.DATABASE_URL)
        await init_beanie(
        database=client.get_default_database(),document_models=[Retrosynthese, CommentDoc])
    class Config:
        env_file = ".env"

class Database:
    def __init__(self, model):
        self.model = model
    async def save(self, document) -> None:
        await document.create()
        return
    async def get(self, id: PydanticObjectId) -> Any:
        doc = await self.model.get(id)
        if doc:
            return doc
        return False
    async def getOne(self, id:str )->Any:
        docs = await self.model.find_one(self.model.identifiant == id)
        return docs
    async def getOneSmile(self, smile:str )->Any:
        docs = await self.model.find_one(self.model.smile == smile)
        return docs
    async def get_allF(self, filtre: str) -> List[Any]:
        docs = await self.model.find(RegEx(self.model.identifiant,filtre)).to_list()
        return docs
    async def get_all(self) -> List[Any]:
        docs = await self.model.find_all().to_list()
        return docs
    async def update(self, id: PydanticObjectId, body: BaseModel) -> Any:
        doc_id = id
        des_body = body.dict()
        des_body = {k:v for k,v in des_body.items() if v is not None}
        update_query = {"$set": {
            field: value for field, value in 
            des_body.items()
        }}
        doc = await self.get(doc_id)
        if not doc:
            return False
        await doc.update(update_query)
        return doc
    async def delete(self, id: PydanticObjectId) -> bool:
        doc = await self.get(id)
        if not doc:
            return False
        await doc.delete()
        return True

Explications : il est important pour des manipulations classiques d’insérer beanie.operator qui nous permettra d’accéder à des fonctions spécifiques pour la mise en place de filtre. Dans cette classe, on a mis en place la généricité des modèles utilisés dans le projet. Ce qui permet d’avoir les mêmes fonctions pour les différents modèles.

Explications de la partie initialize de la database :

async def initialize_database(self):
        client = AsyncIOMotorClient(self.DATABASE_URL)
        await init_beanie(
        database=client.get_default_database(),
                      document_models=[Retrosynthese, CommentDoc])

Dans l’initialisation de la database, on précise les modèles de données que l’on souhaite manipuler. Ce qui dans le code sera définie par l’instruction suivante :

  • retro_database=Database(Retrosynthese) # permet d’ouvrir un descripteur sur la collection retrosynthese
  • comments_database=Database(CommentDoc) # permet d’ouvrir un descripteur sur la collection CommenDoc
Voir les modèles

class Retrosynthese(Document):
    identifiant: str
    date: datetime
    smile: str
    nbroutes: int
    class Settings:
       name="retrosynthese"
    class Config:
        json_schema_extra = {
            "example": {
                "identifiant": "74017a01-ffdb-42fe-80ba-ad77f2fe2750",
                "date":"01/01/2026",
                "smile": "O=C(O)N1Cc2ccc(-c3cnc4cnccn34)cc2C1",
                "nbroutes": 0
            }
        }
class CommentDoc(Document):
    identifiant: str
    date: datetime
    texte: str
    class Settings:
       name="comments"
    class Config:
        json_schema_extra = {
            "example": {
                "identifiant": "74017a01-ffdb-42fe-80ba-ad77f2fe2750_001",
                "date":datetime.now(),
                "texte": "commentaires ",
            }
        }

Explications des méthodes associées à cette classe database et l’utilisation des opérateurs : l’attribut Settings de la classe du modèle permet d’indiquer la collection ciblée qui va contenir les données.

Exemple de mise en place d’agrégation