在SQLAlchemy ORM中使用二级连接

1 投票
2 回答
7793 浏览
提问于 2025-04-18 16:34

我正在尝试建立一个多对多的关系,这个关系是通过一个中间表连接三个表。具体来说,我有两个文件,一个是用来定义模型的(model.py),另一个是用来定义架构的(schema.py)。它们的内容如下:

model.py

import schema
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import *



class AbstractBase(object):
    def __repr__(self):
        macros = ["%s=%s" % (key, getattr(self, key, None)) for key in self.__table__.columns.keys()]
        rep = "<%s(%s)>" % (self.__class__.__name__, str.join(', ', macros))
        return rep

Base = declarative_base(cls=AbstractBase)


class A(Base):
    __table__ = schema.a_table
    dees = relationship("D", 
                       secondary=schema.b_table,
                       primaryjoin="A.a_id==b_table.c.a_id",
                       secondaryjoin="b_table.c.c_id==D.d_id")
    cees = relationship("C", 
                         secondary=schema.b_table,
                         primaryjoin="A.a_id==schema.b_table.c.a_id",
                         secondaryjoin="b_table.c.d_id==C.c_id",
                         backref="a_collection")

class C(Base):
    __table__ = schema.c_table

class D(Base):
    __table__ = schema.d_table

schema.py

from sqlalchemy import *
from sqlalchemy.dialects.mysql import *

metadata = MetaData()

a_table = Table(
    'a',
    metadata,
    Column("a_id", INTEGER(), primary_key=True, nullable=False),
    Column("date", DATETIME(timezone=True)),
)

b_table = Table(
    'shipment_runs',
    metadata,
    Column("a_id", ForeignKey("a.a_id"), primary_key=True,),
    Column("c_id", ForeignKey("c.c_id"), primary_key=True),
    Column("d_id", ForeignKey("d.d_id")),
)

c_table = Table(
    'c',
    metadata,
    Column('c_id', INTEGER(), primary_key=True, nullable=False),
    Column('name', VARCHAR(64), unique=True),
)

d_table = Table(
    'd',
    metadata,
    Column('d_id', INTEGER(), primary_key=True, nullable=False)
)

不过,当我尝试创建这个关系时,出现了一个错误:

sqlalchemy.exc.InvalidRequestError: 在初始化映射器 Mapper|A|a 时,表达式 'A.a_id==b_table.c.a_id' 找不到名称(“name 'b_table' is not defined”)。如果这是一个类名,请考虑在定义完所有相关类后再将这个关系添加到类中。

请问有没有办法让我更改导入的内容,或者让映射器知道架构模块中的对象呢?

2 个回答

1

一般来说,你可以在字符串中使用的名字,或者直接用实际的引用,不用字符串。

primaryjoin="A.a_id==shipment_runs.c.a_id",

primaryjoin=schema.a_table.c.a_id==schema.b_table.c.a_id,

不过,既然你在表里设置了ForeignKey,SQLAlchemy聪明得很,简单的关系你甚至不需要用到连接,只要用secondary就可以了。

c_list = relationship("C", secondary=schema.b_table, backref="a_list")

(我觉得你例子里的"C"和"D"是不是搞错了?)

4

我通过以下方式解决了这个问题:

class B(Base):
    __table__ = schema.b_table


class A(Base):
    __table__ = schema.a_table
    dees = relationship("D", 
                      secondary=b.__table__,
                      primaryjoin="A.a_id==B.a_id",
                      secondaryjoin="B.c_id==D.d_id")
    cees = relationship("C", 
                        secondary=B.__table__,
                        primaryjoin="A.a_id==B.a_id",
                        secondaryjoin="B.d_id==C.c_id",
                        backref="a_collection")

所有的功劳都要归功于这个问题:

SQLAlchemy 关系错误:对象没有属性 'c'

撰写回答