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

发布于 2024-08-09 07:04:19 字数 2328 浏览 1 评论 0原文

我已经有一个现有的数据库,并且想使用 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 未在实例级别实现属性 u'date'。

我尝试添加隐式外键引用 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。 感谢您的任何建议。

I've an already existing database and want to access it using SQLAlchemy. Because, the database structure's managed by another piece of code (Django ORM, actually) and I don't want to repeat myself, describing every table structure, I'm using autoload introspection. I'm stuck with a simple concrete table inheritance.

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

Here is the code, with table SQL descritions as docstrings:

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}

The actual tables have additional columns, but this is completely irrelevant to the question, so in attempt to minimize the code I've simplified everything just to the core.

The problem is, when I run this:

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

The resulting SQL is meaningless (note the lack of join condidion):

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

And when I'm trying to access payment.date I get the following error: Concrete Mapper|FooPayment|payments_foo does not implement attribute u'date' at the instance level.

I've tried adding implicit foreign key reference id = Column('payment_ptr_id', Integer, ForeignKey('payments_payment.id'), primary_key=True) to FooPayment without any success. Trying print session.query(Payment).first().user works (I've omited User class and commented the line) perfectly, so FK introspection works.

How can I perform a simple query on FooPayment and access Payment's values from resulting instance?

I'm using SQLAlchemy 0.5.3, PostgreSQL 8.3, psycopg2 and Python 2.5.2.
Thanks for any suggestions.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

无需解释 2024-08-16 07:04:19

您的表结构与联合表继承中使用的类似,但它们肯定不对应于具体的表继承,其中父类的所有字段都在子类的表中重复。现在,您有一个子类,其字段少于父类,并且具有对父类实例的引用。切换到联合表继承(并在您的条件中使用 FooPayment.amount 或放弃继承以支持简单聚合(参考)。

按其他模型中的字段过滤不会自动添加联接条件。尽管对于您的示例来说,在 join 中应该使用什么条件是显而易见的,但通常无法确定这种条件,这就是为什么您必须定义引用 Payment 的关系属性并在中使用其 has() 方法。过滤以获得正确的连接条件。

Your table structures are similar to what is used in joint table inheritance, but they certainly don't correspond to concrete table inheritance where all fields of parent class are duplicated in the table of subclass. Right now you have a subclass with less fields than parent and a reference to instance of parent class. Switch to joint table inheritance (and use FooPayment.amount in your condition or give up with inheritance in favor of simple aggregation (reference).

Filter by a field in other model doesn't automatically add join condition. Although it's obvious what condition should be used in join for your example, it's not possible to determine such condition in general. That's why you have to define relation property referring to Payment and use its has() method in filter to get proper join condition.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文