{{tag>dev python sql sqlalchemy relation}} ====== Flask-SQLAlchemy : Définir une relation d'agrégation ====== La relation d'agrégation est une forme particulière de [[dev:python:flask:flask-sqlalchemy:relation_one-to-many|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 : {{mcd_relation_agregation.png}} Le symbole UML de l'agrégation {{symbole_agregation.jpg|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. {{mld_relation_agregation.png}} Le détail des règles de traduction des associations dans le MLD est abordé dans le cours [[cours:informatique:dev:db:modeliser_bases_de_donnees:340_transformer_associations_du_diagramme_de_classes|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 : * À 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" in {packet.warehouse.name}>") ... in CentralRepository> in CentralRepository> 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 ===== * [[https://fr.wikipedia.org/wiki/Agr%C3%A9gation_(programmation)|Relation d'agrégation (wikipedia.org) ]]