8

アップデート:

この問題を抱えている人にとっては、最新の SQLAlchemyでこの動作が修正されています。

元の問題:

アソシエーション プロキシを正しく更新するのに問題があります。

ここでモデル例を使用: http://docs.sqlalchemy.org/en/rel_0_7/orm/extensions/associationproxy.html#simplifying-association-objects

ただし、次の行で UserKeyword を変更します。

keyword = relationship("Keyword", backref=backref("user_keywords", cascade="all, delete-orphan"))

これをキーワードに追加します:

users = association_proxy('user_keywords', 'user')

したがって、キーワード インスタンスにはユーザーのリストがあります。

以下は期待どおりに機能します。

>>> rory = User("rory")
>>> session.add(rory)
>>> chicken = Keyword('chicken')
>>> session.add(chicken)
>>> rory.keywords.append(chicken)
>>> chicken.users
[<__main__.User object at 0x1f1c0d0>]
>>> chicken.user_keywords
[<__main__.UserKeyword object at 0x1f1c450>]

しかし、除去は奇妙なことをします。アソシエーション プロキシ リストから削除するには、次のようにします。

>>> rory.keywords.remove(chicken)

SA が外部キー列の 1 つを NULL に設定しようとすると、整合性エラーが発生します。

これを行う:

>>> rory.user_keywords.remove(rory.user_keywords[0])

結果は次のようになります。

>>> chicken.users
[None]

明らかな何かを見逃しましたね。

4

1 に答える 1

7

UserKeyword永続化するには、 と の両方に同時にKeyword関連付ける必要があります。とUserに関連付けた後、コレクションから削除しても、 と関連付けられたままです。 UserKeywordUser.user_keywordsKeyword

>>> rory.keywords.remove(chicken)

# empty as we expect
>>> rory.user_keywords
[]   

# but the other side, still populated.  UserKeyword 
# has no User, but still has Keyword
>>> chicken.user_keywords
[<__main__.UserKeyword object at 0x101748d10>]

# but the User on that UserKeyword is None
>>> chicken.user_keywords[0].user is None
True

# hence accessing the "association" gives us None
# as well
>>> chicken.users
[None]

したがって、これをすぐに flush() すると、オブジェクトのUserKeyword準備はできていますが、オブジェクトがないUserため、NULL エラーが発生します。Keyword.user_keywords INSERT 時に、オブジェクトは、または User.user_keywordsコレクションに関連付けられていない限り、「孤立」しているとは見なされません。INSERT が生成されず、オブジェクトが忘れられてdel chicken.user_keywords[0]いることがわかるか、またはそれと同等のことを言う場合にのみです。UserKeyword

オブジェクトを「rory」から削除する前にデータベースにフラッシュすると、状況が変わります。はUserKeyword永続的になり、「rory.keywords」から「chicken」を削除すると、「delete-orphan」イベントが発生し、オブジェクトに関連付けられているにもかかわらず、削除されます。UserKeywordKeyword

rory.keywords.append(chicken)

session.flush()

rory.keywords.remove(chicken)

session.flush()

次の SQL が表示されます。

INSERT INTO "user" (name) VALUES (%(name)s) RETURNING "user".id
{'name': 'rory'}

INSERT INTO keyword (keyword) VALUES (%(keyword)s) RETURNING keyword.id
{'keyword': 'chicken'}

INSERT INTO user_keyword (user_id, keyword_id, special_key) VALUES (%(user_id)s, %(keyword_id)s, %(special_key)s)
{'keyword_id': 1, 'special_key': None, 'user_id': 1}

DELETE FROM user_keyword WHERE user_keyword.user_id = %(user_id)s AND user_keyword.keyword_id = %(keyword_id)s
{'keyword_id': 1, 'user_id': 1}

さて、合理的な人は「それは矛盾していませんか?」と尋ねるでしょう。現時点では、「絶対に」と言うでしょう。テストケースを調べて、この動作の違いの根拠を確認する必要があります。コードで、このように発生する理由を特定しました。「孤立した」と見なされる方法のこの違いを確信しています。 「保留中」オブジェクトと「永続的」オブジェクトは意図的なものですが、この特定の順列では明らかに奇妙な結果が生じます。実行可能なものを見つけることができれば、これを 0.8 で変更する可能性があります。

編集: http://www.sqlalchemy.org/trac/ticket/2655は、私が考えなければならない問題をまとめたものです。具体的には、この動作のテストがあり、その起源までさかのぼる必要があります。

于 2013-01-23T01:45:06.547 に答える