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
. 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.
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.
from flask import Flask from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy() class Warehouse(db.Model): __tablename__ = 'warehouse' id = db.Column(db.Integer, primary_key=True, index=True) name = db.Column(db.String(80), unique=True, index=True) class Packet(db.Model): __tablename__ = 'packet' id = db.Column(db.Integer, primary_key=True, index=True) sender = db.Column(db.String(80)) recipient = db.Column(db.String(80)) warehouse_id = db.Column(db.Integer, db.ForeignKey('warehouse.id'), unique=False, nullable=True) warehouse = db.relationship("Warehouse", backref="packets") app = Flask(__name__) # Le chemin vers la base SQLite est relatif au dossier de l'application flask app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///database.db" # Initialiser l'application Flask par l'intermédiaire de l'extension db.init_app(app)
Deux point importants à relever :
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é.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