python - sqlalchemy 中使用反射和声明性语法的一对多关系定义给出了连接条件错误

标签 python sqlalchemy

我正在尝试在现有数据库上设置一对多关系。

简化的 DDL 是:

create table accnt (
  code varchar(20) not null
  , def varchar(100)
  , constraint pk_accnt primary key (code)
);

commit;

create table slorder (
  code varchar(20) not null
  , def varchar(100)
  , dt date
  , c_accnt varchar(20) not null
  , constraint pk_slorder primary key (code)
  , constraint fk_slorder_accnt foreign key (c_accnt)
     references accnt (code)
     on update cascade on delete cascade
);

commit;

SqlAlchemy 代码:

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

engine = create_engine('firebird://sysdba:masterkey@127.0.0.1/d:\\prj\\db2\\makki.fdb?charset=WIN1254', echo=False)
Base = declarative_base()
Base.metadata.bind = engine

class Accnt(Base):
    __tablename__ = 'accnt'
    __table_args__ = {'autoload': True}

    defi = Column('def', String(100))

class SlOrder(Base):
    __tablename__ = 'slorder'
    __table_args__ = {'autoload': True}

    defi = Column("def", String(100))
    accnt = relationship('Accnt', backref='slorders')

给出

sqlalchemy.exc.ArgumentError: Could not determine join condition between parent/child tables on relationship SlOrder.accnt.  Specify a 'primaryjoin' expression.  If 'secondary' is present, 'secondaryjoin' is needed as well.

错误。

我对此问题可能的解决方案是:

1

class SlOrder(Base):
    __tablename__ = 'slorder'
    __table_args__ = {'autoload': True}

    defi = Column("def", String(100))
    c_accnt = Column("c_accnt", String(20), ForeignKey('accnt.code'))

    accnt = relationship('Accnt', backref='slorders')

但是这种方法需要我手动添加每个外键约束列,这会导致反射变得无用。 (因为我有许多列引用其他表。)

2

class SlOrder(Base):
    __table__ = Table('accnt', metadata, autoload = True, autoload_with=engine)
    accnt = relationship('Accnt', backref='slorders', primaryjoin=(__table__.c_accnt==Accnt.code))

这种方法还有另一个后果(请参阅 my previous question )

那我错过了什么?使用反射和声明性语法定义关系的最佳方法是什么?

编辑:

我发现,如果子表只有一个对父表的引用,SqlAlchemy 就会查找并建立关系。

但是如果子表有多个引用:

create table slorder (
  code varchar(20) not null
  , def varchar(100)
  , dt date
  , c_accnt varchar(20) not null
  , c_accnt_ref  varchar(20) 
  , constraint pk_slorder primary key (code)
  , constraint fk_slorder_accnt foreign key (c_accnt)
     references accnt (code)
     on update cascade on delete cascade

  , constraint fk_slorder_accnt_ref foreign key (c_accnt_ref)
     references accnt (code)
     on update cascade on delete no action
);

出现上述错误。

那么,如果两个表之间存在多个关系,SqlAlchemy 是否会出现预期的错误行为?

最佳答案

我认为您必须在子表中添加ForeignKey

通过定义ForeignKey,你可以给c_accnt赋值,也可以给accnt赋值parent对象。

sqlalchemy 在内部触发您在primaryjoin 中编写的查询。如果没有外键,则模型无法理解它必须在哪个字段上运行查询。

您可以使用任意两种方式。但我个人更喜欢 ForeignKeyrelation 而不是 ForeignKey。这样您就必须编写更多代码,但是,它可以灵活地直接分配值和对象。

关于python - sqlalchemy 中使用反射和声明性语法的一对多关系定义给出了连接条件错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7681701/

相关文章:

python - Django、带递归的 Celery 和 Twitter API

python - Pandas 计算 str 系列中的频率

python - 将字典绑定(bind)到 SQLAlchemy 模型以进行更新

python - Travis-CI 读取 pull 请求的内容并更新存储库的 README.md

python - 为什么cv2转换后的灰度图仍然有3个 channel ?

python 字符串的正则表达式

python - 是否值得使用 sqlalchemy-migrate ?

sqlalchemy - 关闭并重新连接 SQLAlchemy session 的数据库连接?

python - SQLAlchemy 关系 : Find instances that have one or more attribute in common

python - 在 Pandas 中使用 SQLAlchemy 清理数据库连接