diff --git a/relationship/one_to_many.2.dynamic.py b/relationship/one_to_many.2.dynamic.py new file mode 100644 index 0000000000000000000000000000000000000000..bdcdfa7faad60259e30a0d0b783c7c32452b1442 --- /dev/null +++ b/relationship/one_to_many.2.dynamic.py @@ -0,0 +1,81 @@ +""" +https://docs.sqlalchemy.org/en/14/orm/basic_relationships.html + +To establish a bidirectional relationship in one-to-many, where the “reverse†side is a +many to one, specify an additional relationship() and connect the two using the +relationship.back_populates parameter. +""" +from sqlalchemy import create_engine +from sqlalchemy import Column, ForeignKey, Integer, String +from sqlalchemy.orm import declarative_base, relationship +from sqlalchemy.orm.session import Session + +Base = declarative_base() + + +class Parent(Base): + __tablename__ = "parent" + + id = Column(Integer, primary_key=True) + name = Column(String) + + # A "noload" relationship never loads from the database, even when accessed. + # Below, the children collection is fully writeable, and changes to it will + # be persisted to the database as well as locally available for reading at + # the time they are added. + # https://docs.sqlalchemy.org/en/14/orm/collections.html#setting-noload-raiseload + + # MAIS : Normal applications should not have to use "noload" for anything !!! + # https://github.com/sqlalchemy/sqlalchemy/discussions/7377 + children = relationship("Child", lazy="dynamic") + + def __repr__(self): + return f"<Parent (name={self.name}, children={self.children})>" + + +class Child(Base): + __tablename__ = "child" + + id = Column(Integer, primary_key=True) + name = Column(String) + parent_id = Column(Integer, ForeignKey("parent.id")) + + def __repr__(self): + return f"<Child (name={self.name})>" + + +if __name__ == "__main__": + engine = create_engine("sqlite:///one_to_many.2.dynamic.db", echo=False) + + Base.metadata.drop_all(engine) + Base.metadata.create_all(engine) + + # cf ../basic-session.1.4.py + with Session(engine) as session: + jack = Parent(name="Jack") + + alice = Child(name="Alice") + john = Child(name="John") + + jack.children = [john, alice] + + with session.begin(): + session.add(jack) + session.add(john) + session.add(alice) + + print(jack) + # <Parent (name=Jack, children=SELECT child.id AS child_id, child.name AS child_name, child.parent_id AS child_parent_id # noqa E501 + # FROM child + # WHERE ? = child.parent_id)> + + print(jack.children[:2]) + # [<Child (name=John)>, <Child (name=Alice)>] + + print(jack) + # <Parent (name=Jack, children=SELECT child.id AS child_id, child.name AS child_name, child.parent_id AS child_parent_id # noqa E501 + # FROM child + # WHERE ? = child.parent_id)> + + print(alice) + # <Child (name=Alice)> diff --git a/relationship/one_to_many.2.noload.py b/relationship/one_to_many.2.noload.py new file mode 100644 index 0000000000000000000000000000000000000000..3516d17d7b7882e22c655a22c1460f62a7c1aad9 --- /dev/null +++ b/relationship/one_to_many.2.noload.py @@ -0,0 +1,73 @@ +""" +https://docs.sqlalchemy.org/en/14/orm/basic_relationships.html + +To establish a bidirectional relationship in one-to-many, where the “reverse†side is a +many to one, specify an additional relationship() and connect the two using the +relationship.back_populates parameter. +""" +from sqlalchemy import create_engine +from sqlalchemy import Column, ForeignKey, Integer, String +from sqlalchemy.orm import declarative_base, relationship +from sqlalchemy.orm.session import Session + +Base = declarative_base() + + +class Parent(Base): + __tablename__ = "parent" + + id = Column(Integer, primary_key=True) + name = Column(String) + + # A "noload" relationship never loads from the database, even when accessed. + # Below, the children collection is fully writeable, and changes to it will + # be persisted to the database as well as locally available for reading at + # the time they are added. + # https://docs.sqlalchemy.org/en/14/orm/collections.html#setting-noload-raiseload + + # MAIS : Normal applications should not have to use "noload" for anything !!! + # https://github.com/sqlalchemy/sqlalchemy/discussions/7377 + children = relationship("Child", lazy="noload") + + def __repr__(self): + return f"<Parent (name={self.name}, children={self.children})>" + + +class Child(Base): + __tablename__ = "child" + + id = Column(Integer, primary_key=True) + name = Column(String) + parent_id = Column(Integer, ForeignKey("parent.id")) + + def __repr__(self): + return f"<Child (name={self.name})>" + + +if __name__ == "__main__": + engine = create_engine("sqlite:///one_to_many.2.noload.db", echo=False) + + Base.metadata.drop_all(engine) + Base.metadata.create_all(engine) + + # cf ../basic-session.1.4.py + with Session(engine) as session: + jack = Parent(name="Jack") + + alice = Child(name="Alice") + john = Child(name="John") + + jack.children = [john, alice] + + with session.begin(): + session.add(jack) + session.add(john) + session.add(alice) + + print(jack) + # <Parent (name=Jack, children=[<Child (name=John)>, <Child (name=Alice)>])> + + print(jack) + # <Parent (name=Jack, children=[])> + print(alice) + # <Child (name=Alice)> diff --git a/relationship/one_to_many.dictionary.py b/relationship/one_to_many.dictionary.py new file mode 100644 index 0000000000000000000000000000000000000000..87e2d57b8a1da061f0d74bd7dc13f321af7890ca --- /dev/null +++ b/relationship/one_to_many.dictionary.py @@ -0,0 +1,68 @@ +""" +https://docs.sqlalchemy.org/en/14/orm/basic_relationships.html +https://docs.sqlalchemy.org/en/14/orm/collections.html#customizing-collection-access + +ATTENTION aux changements de la clé du dictionnaire +https://docs.sqlalchemy.org/en/14/orm/collections.html#dealing-with-key-mutations-and-back-populating-for-dictionary-collections +""" +from sqlalchemy import create_engine +from sqlalchemy import Column, ForeignKey, Integer, String +from sqlalchemy.orm import declarative_base, relationship +from sqlalchemy.orm.collections import attribute_mapped_collection +from sqlalchemy.orm.session import Session + +Base = declarative_base() + + +class Item(Base): + __tablename__ = "item" + + id = Column(Integer, primary_key=True) + notes = relationship( + "Note", + collection_class=attribute_mapped_collection("keyword"), + backref="item", + cascade="all, delete-orphan", + ) + + def __repr__(self): + return f"<Item (name={self.id}, notes={self.notes})>" + + +class Note(Base): + __tablename__ = "note" + + id = Column(Integer, primary_key=True) + keyword = Column(String) + text = Column(String) + + item_id = Column(Integer, ForeignKey("item.id")) + + def __repr__(self): + return f"<Node (keyword={self.keyword}, text={self.text})>" + + +if __name__ == "__main__": + engine = create_engine("sqlite:///one_to_many.dictionary.db", echo=False) + + Base.metadata.drop_all(engine) + Base.metadata.create_all(engine) + + # cf ../basic-session.1.4.py + with Session(engine) as session: + item = Item() + n1 = Note(keyword="a", text="atext") + n1.item = item + n2 = Note(keyword="b", text="btext") + n2.item = item + + with session.begin(): + session.add(item) + session.add(n1) + session.add(n2) + + print(item) + # <Item (name=1, notes={'a': <Node (keyword=a, text=atext)>, 'b': <Node (keyword=b, text=btext)>})> # noqa E501 + + print(n1) + # <Node (keyword=a, text=atext)>