Определение отношения один ко многим с отражением и декларативным синтаксисом в sqlalchemy дает ошибку условия соединения - PullRequest
1 голос
/ 07 октября 2011

Я пытаюсь установить отношение «один ко многим» в существующей базе данных.

Упрощенный 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))

У этого подхода есть еще одно последствие (см. мой предыдущийвопрос )

Так чего мне не хватает?Каков наилучший способ определить отношения, используя рефлексию и декларативный синтаксис?

EDIT:

Я понял, что 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 выдаст ошибку, если между двумя таблицами существует более одного отношения?

Ответы [ 2 ]

2 голосов
/ 07 октября 2011

Я думаю, вам нужно добавить ForeignKey в дочернюю таблицу.

С помощью определения ForeignKey вы можете присвоить значение c_accnt, а также присвоить объект родительского элемента accnt.

Внутренне sqlalchemy запускает запрос, который вы написали в primaryjoin.Если внешнего ключа нет, модель не может понять, в каком поле она должна выполнить запрос.

Вы можете использовать любой способ.Но я лично предпочитаю ForeignKey и relation ForeignKey.Таким образом, вам придется написать еще немного кода, но это даст гибкость для непосредственного присвоения значения и объекта.

1 голос
/ 07 октября 2011

Я думаю, что ваш код должен автоматически отражать 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. Если нет, попробуйте выяснить, почему.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...