Ben*_*Ben 5 python many-to-many sqlalchemy
我有一种类似于此处描述的多对多关系。请注意,我的关联表包含一个extra_data字段..
class Association(Base):
__tablename__ = 'association'
left_id = Column(ForeignKey('left.id'), primary_key=True)
right_id = Column(ForeignKey('right.id'), primary_key=True)
extra_data = Column(String(50))
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship("Child", secondary="association", back_populates="parents")
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
parents = relationship("Parent", secondary="association", back_populates="children")
Run Code Online (Sandbox Code Playgroud)
如果我想获取特定的父对象及其子对象,我可以这样做
db_parent = db.query(Parent).where(Parent.id == 1).first()
print(db_parent.children[0].id) # works fine
Run Code Online (Sandbox Code Playgroud)
但是,该extra_data字段不作为子项的属性包含在内。
print(db_parent.children[0].extra_data)
Run Code Online (Sandbox Code Playgroud)
AttributeError:“Child”对象没有属性“extra_data”
我如何编写获取父级的子级,以便将其extra_data作为属性包含在内?
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import declarative_base, relationship, Session
# Make the engine
engine = create_engine("sqlite+pysqlite:///:memory:", future=True, echo=False)
# Make the DeclarativeMeta
Base = declarative_base()
class Association(Base):
__tablename__ = 'association'
left_id = Column(ForeignKey('left.id'), primary_key=True)
right_id = Column(ForeignKey('right.id'), primary_key=True)
extra_data = Column(String(50))
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship("Child", secondary="association", back_populates="parents")
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
parents = relationship("Parent", secondary="association", back_populates="children")
# Create the tables in the database
Base.metadata.create_all(engine)
# Test it
with Session(bind=engine) as session:
# add parents
p1 = Parent()
session.add(p1)
p2 = Parent()
session.add(p2)
session.commit()
# add children
c1 = Child()
session.add(c1)
c2 = Child()
session.add(c2)
session.commit()
# map children to parents
a1 = Association(left_id=p1.id, right_id=c1.id, extra_data='foo')
a2 = Association(left_id=p1.id, right_id=c2.id, extra_data='bar')
a3 = Association(left_id=p2.id, right_id=c2.id, extra_data='baz')
session.add(a1)
session.add(a2)
session.add(a3)
session.commit()
with Session(bind=engine) as session:
db_parent = session.query(Parent).where(Parent.id == 1).first()
print(db_parent.children[0].id)
print(db_parent.children[0].extra_data)
Run Code Online (Sandbox Code Playgroud)
使用 SQLAlchemy 无法完全按照您想要的方式完成您所要求的操作。事实上,who 中的项目Parent.children应该是Child类的实例。如果您的子类具有extra_data从关联表加载的属性,那么它将引用其父类中的哪个属性?
我试图解释的是,您希望在 Child 中拥有的对“extra_data”的隐式引用只有在从父对象引用 Child 对象时才有意义。
例如,想象以下场景
session.add_all(
Association(left=parent_a.id, right=child.id, extra_data="hello")
Association(left=parent_b.id, right=child.id, extra_data="world")
)
Run Code Online (Sandbox Code Playgroud)
您期望在哪个父级元数据中child.extra_data?
而且大多数时候,如果需要一个对象作为关联表,就意味着这个对象本身就有意义。所以你不应该试图隐藏它。看看下面的具体例子
class Account(Base):
__tablename__ = "accounts"
id = Column(Integer, primary_key=True)
username = Column(String(10), nullable=False)
groups = relationship("Membership", back_populates="account")
class Group(Base):
__tablename__ = "groups"
id = Column(Integer, primary_key=True)
name = Column(String(10), nullable=False)
members = relationship("Membership", back_populates="group")
class Membership(Base):
"""Membership is our association table here"""
__tablename__ = "memberships"
id = Column(Integer, primary_key=True)
account_id = Column(Integer, ForeignKey("accounts.id"))
account = relationship("Account", back_populates="groups")
group_id = Column(Integer, ForeignKey("groups.id"))
group = relationship("Group", back_populates="members")
# extra data embed in association table
role = Column(String(10), nullable=False)
Base.metadata.create_all()
# create user "toto" that belongs to group "Funny people" with role "joker"
toto = Account(username="toto")
funny_people = Group(name="Funny people")
session.add(Membership(account=toto, group=funny_people, role="joker"))
session.commit()
Run Code Online (Sandbox Code Playgroud)
请注意两种方法之间的差异。在这里,Account.groups 包含成员身份,而不是直接的 Group 对象。然后你可以这样使用它:
toto = session.query(Account).first()
toto.username
toto.groups[0].group.name
toto.groups[0].role
Run Code Online (Sandbox Code Playgroud)
我知道这并不完全是您所要求的,但这可能是您可以拥有的最接近的结果,而不会引入会干扰应用程序正常运行的奇怪逻辑
| 归档时间: |
|
| 查看次数: |
2399 次 |
| 最近记录: |