繁体   English   中英

如何使用 SQLAlchemy 建立多对多关系:一个很好的例子

[英]How to build many-to-many relations using SQLAlchemy: a good example

我已经阅读了有关构建多对多关系的 SQLAlchemy 文档和教程,但是当关联表包含超过 2 个外键时,我无法弄清楚如何正确执行此操作。

我有一个项目表,每个项目都有很多细节。 许多项目的详细信息可以相同,因此项目和详细信息之间存在多对多关系

我有以下几点:

class Item(Base):
    __tablename__ = 'Item'
    id = Column(Integer, primary_key=True)
    name = Column(String(255))
    description = Column(Text)

class Detail(Base):
    __tablename__ = 'Detail'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    value = Column(String)

我的关联表是(它在代码中的其他 2 个之前定义):

class ItemDetail(Base):
    __tablename__ = 'ItemDetail'
    id = Column(Integer, primary_key=True)
    itemId = Column(Integer, ForeignKey('Item.id'))
    detailId = Column(Integer, ForeignKey('Detail.id'))
    endDate = Column(Date)

在文档中,据说我需要使用“关联对象”。 我不知道如何正确使用它,因为它混合了声明性和映射器表单,并且示例似乎不完整。 我添加了以下行:

details = relation(ItemDetail)

作为 Item 类和行的成员:

itemDetail = relation('Detail')

作为关联表的成员,如文档中所述。

当我执行 item = session.query(Item).first() 时,item.details 不是 Detail 对象的列表,而是 ItemDetail 对象的列表。

如何在 Item 对象中正确获取详细信息,即 item.details 应该是 Detail 对象的列表?

从评论中我看到你找到了答案。 但SQLAlchemy文档对于“新用户”而言非常压倒性,而我正在努力解决同样的问题。 所以供将来参考:

ItemDetail = Table('ItemDetail',
    Column('id', Integer, primary_key=True),
    Column('itemId', Integer, ForeignKey('Item.id')),
    Column('detailId', Integer, ForeignKey('Detail.id')),
    Column('endDate', Date))

class Item(Base):
    __tablename__ = 'Item'
    id = Column(Integer, primary_key=True)
    name = Column(String(255))
    description = Column(Text)
    details = relationship('Detail', secondary=ItemDetail, backref='Item')

class Detail(Base):
    __tablename__ = 'Detail'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    value = Column(String)
    items = relationship('Item', secondary=ItemDetail, backref='Detail')

以前的答案对我有用,但我对表 ItemDetail 使用了类基方法。 这是示例代码:

class ItemDetail(Base):
    __tablename__ = 'ItemDetail'
    id = Column(Integer, primary_key=True, index=True)
    itemId = Column(Integer, ForeignKey('Item.id'))
    detailId = Column(Integer, ForeignKey('Detail.id'))
    endDate = Column(Date)

class Item(Base):
    __tablename__ = 'Item'
    id = Column(Integer, primary_key=True)
    name = Column(String(255))
    description = Column(Text)
    details = relationship('Detail', secondary=ItemDetail, backref='Item')

class Detail(Base):
    __tablename__ = 'Detail'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    value = Column(String)
    items = relationship('Item', secondary=ItemDetail, backref='Detail')

像 Miguel 一样,我也在我的连接表中使用声明式方法。 但是,我一直遇到错误,例如

sqlalchemy.exc.ArgumentError:传递给relationship() User.projects 的次要参数<class ' main .ProjectUser'> 必须是Table 对象或其他FROM 子句; 不能直接发送映射类,因为“辅助”中的行独立于映射到同一个表的类而持久化。

通过一些摆弄,我能够想出以下内容。 (请注意,我的课程与 OP 不同,但概念是相同的。)

例子

这是一个完整的工作示例

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 User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True)
    name = Column(String)
    projects = relationship('Project', secondary='project_users', back_populates='users')


class Project(Base):
    __tablename__ = "projects"

    id = Column(Integer, primary_key=True)
    name = Column(String)
    users = relationship('User', secondary='project_users', back_populates='projects')


class ProjectUser(Base):
    __tablename__ = "project_users"

    id = Column(Integer, primary_key=True)
    notes = Column(String, nullable=True)
    user_id = Column(Integer, ForeignKey('users.id'))
    project_id = Column(Integer, ForeignKey('projects.id'))



# Create the tables in the database
Base.metadata.create_all(engine)

# Test it
with Session(bind=engine) as session:

    # add users
    usr1 = User(name="bob")
    session.add(usr1)

    usr2 = User(name="alice")
    session.add(usr2)

    session.commit()

    # add projects
    prj1 = Project(name="Project 1")
    session.add(prj1)

    prj2 = Project(name="Project 2")
    session.add(prj2)

    session.commit()

    # map users to projects
    prj1.users = [usr1, usr2]
    prj2.users = [usr2]

    session.commit()


with Session(bind=engine) as session:

    print(session.query(User).where(User.id == 1).one().projects)
    print(session.query(Project).where(Project.id == 1).one().users)

笔记

  1. secondary参数中引用表名,如secondary='project_users'而不是secondary=ProjectUser
  2. 使用back_populates而不是backref

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM