2016-08-15 1 views
9

私は双方向association_proxyを使用して、プロパティGroup.membersUser.groupsを関連付けています。会員をGroup.membersから削除する際に問題が発生しています。特に、Group.members.removeは、の対応するエントリに代わって、,のエントリを正常に削除しますが、Noneのままになります。より具体的には、以下の(最小限っぽい)代表のコードスニペットは、その最後のアサーションに失敗双方向のassociation_proxyを使用しているときに自動的に削除を伝播する

import sqlalchemy as sa 

from sqlalchemy.orm import Session 
from sqlalchemy.ext.associationproxy import association_proxy 
from sqlalchemy.ext.declarative import declarative_base 


Base = declarative_base() 


class Group(Base): 
    __tablename__ = 'group' 
    id = sa.Column(sa.Integer, autoincrement=True, primary_key=True) 
    name = sa.Column(sa.UnicodeText()) 
    members = association_proxy('group_memberships', 'user', 
      creator=lambda user: GroupMembership(user=user)) 


class User(Base): 
    __tablename__ = 'user' 
    id = sa.Column(sa.Integer, autoincrement=True, primary_key=True) 
    username = sa.Column(sa.UnicodeText()) 
    groups = association_proxy('group_memberships', 'group', 
      creator=lambda group: GroupMembership(group=group)) 


class GroupMembership(Base): 
    __tablename__ = 'user_group' 
    user_id = sa.Column(sa.Integer, sa.ForeignKey('user.id'), primary_key=True) 
    group_id = sa.Column(sa.Integer, sa.ForeignKey('group.id'), primary_key=True) 

    user = sa.orm.relationship(
      'User', 
      backref=sa.orm.backref('group_memberships', cascade="all, delete-orphan")) 
    group = sa.orm.relationship(
      'Group', 
      backref=sa.orm.backref('group_memberships', cascade="all, delete-orphan"), 
      order_by='Group.name') 


if __name__ == '__main__': 
    engine = sa.create_engine('sqlite://') 
    Base.metadata.create_all(engine) 
    session = Session(engine) 

    group = Group(name='group name') 
    user = User(username='user name') 
    group.members.append(user) 
    session.add(group) 
    session.add(user) 
    session.flush() 
    assert group.members == [user] 
    assert user.groups == [group] 
    group.members.remove(user) 
    session.flush() 
    assert group.members == [] 
    assert user.groups == [] # This assertion fails, user.groups is [None] 

私はSQLAlchemy relationship with association_proxy problemsHow can SQLAlchemy association_proxy be used bi-directionally?への回答を追跡しようとしたが、彼らは助けていないようです。

+6

問題ありませんでしたか?いいえ、ご質問ありがとうございます。+1 – enderland

答えて

2

私は何が起こっているのか把握しようとしていたので、事故によってほぼ完全にyour problemを発見しました。

dbにデータがないため、session.commit()を追加しました。データベースがsession.commitいるもの現在のトランザクション((のコミットを受信するまで、変更がディスクに永続的に持続し、または他のトランザクションには見えていない

:それは(リンクの答えから)ことが判明します))。

変更したばかりなので、sqlalchemyは決してデータベースを再クエリしません。これを確認するには、

import logging 
logging.getLogger('sqlalchemy').setLevel(logging.INFO) 
logging.getLogger('sqlalchemy').addHandler(logging.StreamHandler()) 

を追加してください。実行されたすべてのクエリが表示されます。 session.flush()session.commit()に変更してから再実行すると、commitの後に複数のSELECT文が実行されることがわかります。

session.expire(user)またはsession.refresh(user)のいずれかのように見えますが、ユーザーの更新も強制されます。私はそれが明示的に(またはそれが望ましい場合でも)他のオブジェクトに更新を強制的に伝播させる方法があるかどうかはわかりません。

+0

ありがとうございました!あなたは、 '.commit()'がこの問題を修正するのは間違いありません。 しかし、一貫性を達成するためにコミットを余儀なくされるのは望ましくないようです。したがって、より良い解決策が掲載されていない場合は、この回答を1日で受け入れるとマークします。 – mickeyh

+1

@mickeyh 'session.expire_all()'を発行してSQLAlchemyにデータをリロードさせることができますが、これは回避策のようです。 – univerio

+0

@mickeyhまたは 'session.expire(user)'または 'session.refresh(user)'も明らかに機能します。 –

関連する問題