SQLAlchemy декларативное наследование конкретной автозагружаемой таблицы

У меня уже есть база данных, и я хочу получить к ней доступ с помощью SQLAlchemy. Поскольку структура базы данных управляется другим фрагментом кода (на самом деле Django ORM), и я не хочу повторяться, описывая каждую структуру таблицы, я использую autoload самоанализ. Я застрял с простым наследованием конкретной таблицы.

Payment                FooPayment
  + id (PK) <----FK------+ payment_ptr_id (PK)
  + user_id              + foo
  + amount
  + date

Вот код с описаниями таблицы SQL в виде строк документации:

class Payment(Base):
    """
    CREATE TABLE payments(
      id serial NOT NULL,
      user_id integer NOT NULL,
      amount numeric(11,2) NOT NULL,
      date timestamp with time zone NOT NULL,
      CONSTRAINT payment_pkey PRIMARY KEY (id),
      CONSTRAINT payment_user_id_fkey FOREIGN KEY (user_id)
          REFERENCES users (id) MATCH SIMPLE)
    """
    __tablename__ = 'payments'
    __table_args__ = {'autoload': True}
    # user = relation(User)

class FooPayment(Payment):
    """
    CREATE TABLE payments_foo(
      payment_ptr_id integer NOT NULL,
      foo integer NOT NULL,
      CONSTRAINT payments_foo_pkey PRIMARY KEY (payment_ptr_id),
      CONSTRAINT payments_foo_payment_ptr_id_fkey
          FOREIGN KEY (payment_ptr_id)
          REFERENCES payments (id) MATCH SIMPLE)
    """
    __tablename__ = 'payments_foo'
    __table_args__ = {'autoload': True}
    __mapper_args__ = {'concrete': True}

В реальных таблицах есть дополнительные столбцы, но это совершенно не имеет отношения к вопросу, поэтому, пытаясь минимизировать код, я упростил все до основания.

Проблема в том, что когда я запускаю это:

payment = session.query(FooPayment).filter(Payment.amount >= 200.0).first()
print payment.date

Результирующий SQL бессмыслен (обратите внимание на отсутствие условия соединения):

SELECT payments_foo.payment_ptr_id AS payments_foo_payment_ptr_id,
       ... /* More `payments_foo' columns and NO columns from `payments' */
    FROM payments_foo, payments
    WHERE payments.amount >= 200.0 LIMIT 1 OFFSET 0

И когда я пытаюсь получить доступ к payment.date, я получаю следующую ошибку: Concrete Mapper|FooPayment|payments_foo does not implement attribute u'date' at the instance level.

Я безуспешно пытался добавить неявную ссылку на внешний ключ id = Column('payment_ptr_id', Integer, ForeignKey('payments_payment.id'), primary_key=True) в FooPayment. Попытка print session.query(Payment).first().user работает (я пропустил класс User и прокомментировал строку) отлично, поэтому самоанализ FK работает.

Как я могу выполнить простой запрос к FooPayment и получить доступ к значениям Payment из результирующего экземпляра?

Я использую SQLAlchemy 0.5.3, PostgreSQL 8.3, psycopg2 и Python 2.5.2. Спасибо за любые предложения.


person drdaeman    schedule 27.10.2009    source источник


Ответы (1)


Структуры ваших таблиц аналогичны тем, что используются в наследовании совместных таблиц, но они, конечно, не соответствуют наследованию конкретных таблиц, где все поля родительского класса дублируются в таблице подкласса. Прямо сейчас у вас есть подкласс с меньшим количеством полей, чем у родителя, и ссылка на экземпляр родительского класса. Переключитесь на наследование совместных таблиц (и используйте FooPayment.amount в своем условии или откажитесь от наследования в пользу простой агрегации (ссылка).

Фильтрация по полю в другой модели не добавляет автоматически условия соединения. Хотя очевидно, какое условие следует использовать в соединении для вашего примера, определить такое условие в общем случае невозможно. Вот почему вы должны определить свойство отношения, относящееся к платежу, и использовать его метод has() в фильтре, чтобы получить правильное условие соединения.

person Denis Otkidach    schedule 28.10.2009
comment
Благодарю вас! Я, конечно, ошибся между разными типами наследования. Простое удаление {'concrete': True} сделало его присоединенным к наследованию, и оно отлично работает. Спасибо за объяснение. - person drdaeman; 28.10.2009