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