From 596f00c2f20c4f7dad60988303dadd8bf875e3ac Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7oise=20Conil?= <francoise.conil@liris.cnrs.fr>
Date: Thu, 13 Jan 2022 17:23:05 +0100
Subject: [PATCH] =?UTF-8?q?Composite=20Foreign=20Key=20fonctionnel=20(pas?=
 =?UTF-8?q?=20non=20plus=20bien=20document=C3=A9)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 relationship/composite-foreign-key.py | 117 ++++++++++++++++++++++++++
 1 file changed, 117 insertions(+)
 create mode 100644 relationship/composite-foreign-key.py

diff --git a/relationship/composite-foreign-key.py b/relationship/composite-foreign-key.py
new file mode 100644
index 0000000..f86301c
--- /dev/null
+++ b/relationship/composite-foreign-key.py
@@ -0,0 +1,117 @@
+"""
+https://stackoverflow.com/questions/43051051/using-sqlalchemy-with-composite-primary-and-foreign-keys
+
+https://docs.sqlalchemy.org/en/14/orm/declarative_tables.html?highlight=__table_args__#declarative-table-configuration
+
+https://docs.sqlalchemy.org/en/14/orm/basic_relationships.html
+https://docs.sqlalchemy.org/en/14/faq/ormconfiguration.html#i-m-using-declarative-and-setting-primaryjoin-secondaryjoin-using-an-and-or-or-and-i-am-getting-an-error-message-about-foreign-keys
+https://docs.sqlalchemy.org/en/14/orm/relationship_persistence.html
+"""
+from uuid import uuid4
+
+from slugify import slugify
+from sqlalchemy import (
+    Column,
+    ForeignKey,
+    ForeignKeyConstraint,
+    Text,
+    create_engine,
+)
+from sqlalchemy.orm import declarative_base, relationship
+from sqlalchemy.orm.session import Session
+
+Base = declarative_base()
+
+
+class Org(Base):
+    __tablename__ = "org"
+
+    name = Column(Text)
+    orgid = Column(Text, primary_key=True)
+
+    projects = relationship("Project", back_populates="org")
+
+    def __init__(self, name):
+        self.name = name
+        self.orgid = slugify(name)
+
+    def __repr__(self):
+        return f"<Org (name={self.name}, orgid={self.orgid}, projects={self.projects})>"
+
+
+class Project(Base):
+    __tablename__ = "project"
+
+    name = Column(Text)
+    projectid = Column(Text, primary_key=True)
+    orgid = Column(Text, ForeignKey("org.orgid"))
+
+    org = relationship("Org", back_populates="projects")
+
+    streams = relationship("Stream", back_populates="project")
+
+    def __init__(self, name):
+        self.name = name
+        self.projectid = slugify(name)
+
+    def __repr__(self):
+        return (
+            f"<Project (name={self.name}, projectid={self.projectid}, "
+            f"streams={self.streams})>"
+        )
+
+
+class Stream(Base):
+    __tablename__ = "stream"
+
+    uuid = Column(Text, primary_key=True)
+    name = Column(Text, index=True, nullable=False)
+
+    orgid = Column(Text)
+    projectid = Column(Text)
+
+    project = relationship(
+        "Project", foreign_keys=[projectid, orgid], back_populates="streams"
+    )
+
+    __table_args__ = (
+        ForeignKeyConstraint(
+            ["projectid", "orgid"], ["project.projectid", "project.orgid"]
+        ),
+    )
+
+    def __init__(self):
+        self.uuid = str(uuid4())
+
+    def __repr__(self):
+        return (
+            f"<Stream (name={self.name}, uuid={self.uuid}, "
+            f"projectid={self.projectid})>"
+        )
+
+
+if __name__ == "__main__":
+    engine = create_engine("sqlite:///composite-foreign-key.db", echo=False)
+
+    Base.metadata.drop_all(engine)
+    Base.metadata.create_all(engine)
+
+    # cf ../basic-session.1.4.py
+    with Session(engine) as session:
+        o1 = Org(name="Test des clés composites")
+
+        p1 = Project(name="Die straße")
+        p1.org = o1
+
+        s1 = Stream()
+        s1.name = "Il faut maintenant relier le flux au project"
+        s1.project = p1
+
+        with session.begin():
+            session.add(o1)
+            session.add(p1)
+            session.add(s1)
+
+        print(o1)
+        print(p1)
+        print(s1)
-- 
GitLab