SQLAlchemyの宣言的な具体的な自動ロードテーブルの継承
-
06-07-2019 - |
質問
既存のデータベースがあり、SQLAlchemyを使用してアクセスしたい。データベース構造は別のコード(実際にはDjango ORM)によって管理されており、すべてのテーブル構造を記述して繰り返したくないため、 autoload
イントロスペクションを使用しています。単純な具体的なテーブルの継承にこだわっています。
Payment FooPayment
+ id (PK) <----FK------+ payment_ptr_id (PK)
+ user_id + foo
+ amount
+ date
次のコードは、テーブル文字列をドキュメント文字列として示しています。
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を使用しています。 提案をありがとう。
解決
テーブル構造は、ジョイントテーブル継承で使用されるものと似ていますが、親クラスのすべてのフィールドがサブクラスのテーブルで複製される具体的なテーブル継承には対応しません。現在、親よりもフィールドが少ないサブクラスと、親クラスのインスタンスへの参照があります。ジョイントテーブルの継承に切り替えます(条件で FooPayment.amount
を使用するか、単純な集計(参照)を優先して継承を放棄します。
他のモデルのフィールドでフィルタリングしても、結合条件は自動的に追加されません。あなたの例ではjoinでどの条件を使用すべきかは明らかですが、一般的にそのような条件を決定することはできません。そのため、Paymentを参照するリレーションプロパティを定義し、フィルターでその has()
メソッドを使用して適切な結合条件を取得する必要があります。