【问题标题】:SQLAlchemy relationship on many-to-many association table多对多关联表上的 SQLAlchemy 关系
【发布时间】:2012-01-22 23:18:17
【问题描述】:

我正在尝试建立与另一个多对多关系的关系,代码如下所示:

from sqlalchemy import Column, Integer, ForeignKey, Table, ForeignKeyConstraint, create_engine
from sqlalchemy.orm import relationship, backref, scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

supervision_association_table = Table('supervision', Base.metadata,
    Column('supervisor_id', Integer, ForeignKey('supervisor.id'), primary_key=True),
    Column('client_id', Integer, ForeignKey('client.id'), primary_key=True)
)

class User(Base):
    __tablename__ = 'user'

    id = Column(Integer, primary_key=True)

class Supervisor(User):
    __tablename__ = 'supervisor'
    __mapper_args__ = {'polymorphic_identity': 'supervisor'}

    id = Column(Integer, ForeignKey('user.id'), primary_key = True)

    schedules = relationship("Schedule", backref='supervisor')

class Client(User):
    __tablename__ = 'client'
    __mapper_args__ = {'polymorphic_identity': 'client'}

    id = Column(Integer, ForeignKey('user.id'), primary_key = True)

    supervisor = relationship("Supervisor", secondary=supervision_association_table,
                                backref='clients')
    schedules = relationship("Schedule", backref="client")

class Schedule(Base):
    __tablename__ = 'schedule'
    __table_args__ = (
        ForeignKeyConstraint(['client_id', 'supervisor_id'], ['supervision.client_id', 'supervision.supervisor_id']),
    )

    id = Column(Integer, primary_key=True)
    client_id = Column(Integer, nullable=False)
    supervisor_id = Column(Integer, nullable=False)

engine = create_engine('sqlite:///temp.db')
db_session = scoped_session(sessionmaker(bind=engine))
Base.metadata.create_all(bind=engine)

我想做的是将时间表与特定的客户-主管-关系相关联,尽管我还没有找到如何去做。通过 SQLAlchemy 文档,我发现了一些提示,导致了 Schedule-Table 上的 ForeignKeyConstraint。

如何指定关系以使此关联起作用?

【问题讨论】:

  • 拥有单独的 supervisor_association_table 和一个与之具有一对一关系的 Schedule 类的目的是什么?为什么不使用一张表,然后使用关联对象模式? sqlalchemy.org/docs/orm/relationships.html#association-object。在任何情况下,您都需要在此处映射 supervisor_associaiton_table 以便创建与其相关的关系链,然后如果您保留两个单独的“计划”和“监督”表,则与相关项目建立关系链。
  • 客户真的可以有超过1个主管吗?您的 M-N 表建议 yes,但关系(主管)的名称暗示 No
  • @zzzeek 我希望调度类与监督具有一对多的关系,以便一对客户监督可以有许多不同的调度。
  • @van 你是对的,这个名字有误导性。它实际上是一个多对多的关系,因此每一对(如我上面的评论中所述)可以有多个时间表。

标签: python sqlalchemy


【解决方案1】:

您需要映射 supervisor_association_table 以便您可以创建与它的关系。

我可能在这里掩饰了一些东西,但似乎因为你在这里有多对多,所以你真的不能有 Client.schedules - 如果我说 Client.schedules.append(some_schedule),那么 "监督”是指?因此,下面的示例为加入每个 SupervisorAssociation 的 Schedule 集合的那些提供了一个只读的“汇总”访问器。 Association_proxy 扩展用于在方便时隐藏 SupervisionAssociation 对象的详细信息。

from sqlalchemy import Column, Integer, ForeignKey, Table, ForeignKeyConstraint, create_engine
from sqlalchemy.orm import relationship, backref, scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.associationproxy import association_proxy
from itertools import chain

Base = declarative_base()

class SupervisionAssociation(Base):
    __tablename__ = 'supervision'

    supervisor_id = Column(Integer, ForeignKey('supervisor.id'), primary_key=True)
    client_id = Column(Integer, ForeignKey('client.id'), primary_key=True)

    supervisor = relationship("Supervisor", backref="client_associations")
    client = relationship("Client", backref="supervisor_associations")
    schedules = relationship("Schedule")

class User(Base):
    __tablename__ = 'user'

    id = Column(Integer, primary_key=True)

class Supervisor(User):
    __tablename__ = 'supervisor'
    __mapper_args__ = {'polymorphic_identity': 'supervisor'}

    id = Column(Integer, ForeignKey('user.id'), primary_key = True)

    clients = association_proxy("client_associations", "client", 
                        creator=lambda c: SupervisionAssociation(client=c))

    @property
    def schedules(self):
        return list(chain(*[c.schedules for c in self.client_associations]))

class Client(User):
    __tablename__ = 'client'
    __mapper_args__ = {'polymorphic_identity': 'client'}

    id = Column(Integer, ForeignKey('user.id'), primary_key = True)

    supervisors = association_proxy("supervisor_associations", "supervisor", 
                        creator=lambda s: SupervisionAssociation(supervisor=s))
    @property
    def schedules(self):
        return list(chain(*[s.schedules for s in self.supervisor_associations]))

class Schedule(Base):
    __tablename__ = 'schedule'
    __table_args__ = (
        ForeignKeyConstraint(['client_id', 'supervisor_id'], 
        ['supervision.client_id', 'supervision.supervisor_id']),
    )

    id = Column(Integer, primary_key=True)
    client_id = Column(Integer, nullable=False)
    supervisor_id = Column(Integer, nullable=False)
    client = association_proxy("supervisor_association", "client")

engine = create_engine('sqlite:///temp.db', echo=True)
db_session = scoped_session(sessionmaker(bind=engine))
Base.metadata.create_all(bind=engine)

c1, c2 = Client(), Client()
sp1, sp2 = Supervisor(), Supervisor()
sch1, sch2, sch3 = Schedule(), Schedule(), Schedule()

sp1.clients = [c1]
c2.supervisors = [sp2]
c2.supervisor_associations[0].schedules = [sch1, sch2]
c1.supervisor_associations[0].schedules = [sch3]

db_session.add_all([c1, c2, sp1, sp2, ])
db_session.commit()


print c1.schedules
print sp2.schedules

【讨论】:

  • 这基本上就是我的想法,在关联表上有时间表并将其映射到一个类。我没有使用关联代理,尽管我可能会这样做。谢谢你。
猜你喜欢
  • 1970-01-01
  • 2015-03-16
  • 1970-01-01
  • 1970-01-01
  • 2018-11-24
  • 2010-12-25
  • 2011-09-02
  • 2015-08-05
相关资源
最近更新 更多