Outils pour utilisateurs

Outils du site


dev:python:flask:flask-sqlalchemy:relation_agregation

Flask-SQLAlchemy : Définir une relation d'agrégation

La relation d'agrégation est une forme particulière de relation one-to-many. C'est une composition sans appartenance : la suppression d'une instance de la classe regroupant les agrégats n'entraine pas la suppression des agrégats.

Un exemple de relation d'agrégation dans le diagramme UML ci-dessous :

Le symbole UML de l'agrégation Symbole UML de l'agrégation. On place le symbole du côté de la classe qui regroupe les agrégats.

Un entrepôt (Warehouse) peut stocker plusieurs colis (Packet), et un colis est entreposé dans un seul entrepôt : cela définit bien une association un à plusieurs (one to many).

En définissant une association de type agréation, on fait le choix de conserver les colis dans notre base de données même si on supprime notre entrepôt.

MLD et création des contraintes

Pour pouvoir traduire cette relation d'agrégation il va falloir créer les contraintes appropriées lors de l'étape de traduction vers les MLD/MPD.

Le détail des règles de traduction des associations dans le MLD est abordé dans le cours Transformez les associations de votre diagramme de classes UML. Ici on ajoute une clé étrangère à la relation (table) “packet”.

Ci-dessous une application minimale Flask mettant en œuvre le modèle de données.

app.py
  1. from flask import Flask
  2. from flask_sqlalchemy import SQLAlchemy
  3.  
  4. db = SQLAlchemy()
  5.  
  6.  
  7. class Warehouse(db.Model):
  8. __tablename__ = 'warehouse'
  9. id = db.Column(db.Integer, primary_key=True, index=True)
  10. name = db.Column(db.String(80), unique=True, index=True)
  11.  
  12.  
  13.  
  14. class Packet(db.Model):
  15. __tablename__ = 'packet'
  16. id = db.Column(db.Integer, primary_key=True, index=True)
  17. sender = db.Column(db.String(80))
  18. recipient = db.Column(db.String(80))
  19. warehouse_id = db.Column(db.Integer, db.ForeignKey('warehouse.id'), unique=False, nullable=True)
  20. warehouse = db.relationship("Warehouse", backref="packets")
  21.  
  22.  
  23. app = Flask(__name__)
  24.  
  25. # Le chemin vers la base SQLite est relatif au dossier de l'application flask
  26. app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///database.db"
  27.  
  28. # Initialiser l'application Flask par l'intermédiaire de l'extension
  29. db.init_app(app)

Deux point importants à relever :

  • À la ligne 19 on définit la clé étrangère avec l'option nullable=True cela permet au SGDB de remplacer la clé primaire de l’entrepôt par la valeur null lorsque l'entrepôt lié est supprimé.
  • À la ligne 20 on définit l'association entre Warehouse et Packet : le premier argument permet de spécifier le nom de la classe liée et le second permet de définir l'attribut de rappel ainsi Packet.warehouse permettra de retrouver l’entrepôt lié au colis par cette association.

On peut tester rapidement le comportement du modèle avec la commande flask shell (mode interactif dans le contexte de l'application Flask).

>>> # Reinitialisation de la base
>>> db.drop_all()
>>> db.create_all()
>>>
>>> # Création d'un entrepôt
>>> repo = Warehouse(name="CentralRepository")
>>> 
>>> # Création de 3 colis déposés dans cet entrepôt
>>> p1 = Packet(sender="Yoann", recipient="Mariel", warehouse=repo)
>>> p2 = Packet(sender="Annie", recipient="Nathalie", warehouse=repo)
>>> p3 = Packet(sender="Emilie", recipient="Bastien", warehouse=repo)
>>> 
>>> # Enregistre dans la base
>>> db.session.add_all([repo, p1, p2, p3])
>>> db.session.commit()
>>>
>>> # Récupère et affiche les colis
>>> packets = Packet.query.all()
>>> for packet in packets :
...   print(f"<Packet {packet.id} sender={packet.sender} recipient={packet.recipient}> in {packet.warehouse.name}>")
... 
<Packet 1 sender=Yoann recipient=Mariel> in CentralRepository>
<Packet 2 sender=Annie recipient=Nathalie> in CentralRepository>
<Packet 3 sender=Emilie recipient=Bastien> in CentralRepository>
>>>
>>> # Supprimer l'entrepôt
>>> db.session.delete(repo)
>>> db.session.commit()
>>> del repo
>>>
>>> # On récupère un colis et on affiche l’entrepôt lié
>>> packet = Packet.query.first()
>>> print(packet.warehouse)
None

Pour automatiser les tests on pourra s’appuyer sur pytest

Références

dev/python/flask/flask-sqlalchemy/relation_agregation.txt · Dernière modification : 2025/07/22 15:31 de yoann