SQLAlchemy 声明式具体自动加载表继承

3 投票
1 回答
1539 浏览
提问于 2025-04-15 15:25

我有一个已经存在的数据库,想用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.

我尝试在FooPayment中添加隐式外键引用id = Column('payment_ptr_id', Integer, ForeignKey('payments_payment.id'), primary_key=True),但没有成功。尝试print session.query(Payment).first().user可以正常工作(我省略了User类并注释了那行),所以外键的自动加载是有效的。

我该如何对FooPayment执行简单查询,并从结果实例中访问Payment的值呢?

我使用的是SQLAlchemy 0.5.3,PostgreSQL 8.3,psycopg2和Python 2.5.2。感谢任何建议。

1 个回答

4

你的表结构看起来像是联合表继承的样子,但其实并不完全符合具体表继承的方式。在具体表继承中,父类的所有字段都会在子类的表中重复出现。而现在你有一个子类,它的字段比父类少,并且还引用了父类的实例。你可以考虑改用联合表继承(在条件中使用FooPayment.amount),或者放弃继承,选择简单的聚合方式(也就是引用)。

在其他模型中通过某个字段进行过滤,并不会自动添加连接条件。虽然在你的例子中,应该使用什么条件来连接是很明显的,但一般情况下是无法确定这样的条件的。因此,你需要定义一个关系属性,指向Payment,并在过滤时使用它的has()方法,以便得到正确的连接条件。

撰写回答