【问题标题】:One To Many Relationship definition with reflection and declarative syntax in sqlalchemy gives join condition errorsqlalchemy中具有反射和声明性语法的一对多关系定义给出了连接条件错误
【发布时间】:2011-12-02 15:51:23
【问题描述】:

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

简化的 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 的预期行为是否会给出错误?

【问题讨论】:

    标签: python sqlalchemy


    【解决方案1】:

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

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

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

    您可以同时使用任何一种方式。但我个人更喜欢ForeignKeyrelation 而不是ForeignKey。这种方式你必须编写更多的代码,但是它可以灵活地直接分配值加对象。

    【讨论】:

      【解决方案2】:

      我认为您的代码应该自动反映 ForeignKey 并用于关系而不做任何更改。
      不过,只是一些探索这个问题的想法:

      1. 确保您没有多个ForeignKey 指向同一个父表,否则您必须使用primaryjoin 参数指定连接条件,因为SA 无法自动决定使用哪一个。
      2. 确保您确实在slorder 表中定义了ForeignKey(如代码示例所示)
      3. 检查是否可能为某些表定义了非默认schema,也许您需要在表中定义一个_table_args__ = {'schema': 'my_schema'}(只是猜测,因为我不知道firebird所以不知道架构支持真的)
      4. 检查/调试反射步骤:sqlalchemy/dialects/firebird/base.py 具有 get_foreign_keys。检查 SQL 语句fkqry 并直接在您的数据库上执行它,看看它是否反映了您的ForeignKey。如果不是,请尝试找出原因。

      【讨论】:

      • 1.是的,我有 2. Firebird 不支持架构 3. SqlAlchemy 正确找到了我的外键及其属性。
      • 嗯,你确实回答了你的问题。不幸的是,您的原始(简化)代码没有暴露问题,这正是父表存在多个 FK。为了完整起见,我将更新我的答案。
      • 感谢您的 cmets,我会接受您的回答,因为它是正确的 :) 在使用反射和声明式样式时,我找不到设置 primaryjoin 的正确方法,因此我测试了 1. 并且它有效.尝试设置关系只需键入 accnt = relationship('Accnt', backref='slorders', primaryjoin=(Accnt.code==c_accnt)) 并省略列定义会导致 NameError: name 'c_accnt' is not defined 错误。
      • 我认为您可以尝试定义字符串化匹配条件:accnt = relationship('Accnt', backref='slorders', primaryjoin = "slorder.c_accnt==accnt.code")),它可以在不定义 __table__ 的情况下工作,并且仍然只使用 __tablename__
      猜你喜欢
      • 2020-09-18
      • 2011-05-13
      • 2016-08-27
      • 1970-01-01
      • 1970-01-01
      • 2021-11-05
      • 2011-05-30
      • 2010-09-20
      • 2014-05-26
      相关资源
      最近更新 更多