2016-08-15 5 views
9

का उपयोग करते समय स्वचालित रूप से विलोपन प्रसारित करना मैं Group.members और User.groups गुणों को जोड़ने के लिए एक बिडरेक्शनल association_proxy का उपयोग कर रहा हूं। मुझे Group.members से सदस्य को हटाने के साथ समस्याएं आ रही हैं। विशेष रूप से, Group.members.removeGroup.members, से सफलतापूर्वक एक प्रविष्टि को हटा देगा, लेकिन User.groups में इसी प्रविष्टि के स्थान पर None छोड़ देगा।बिडरेक्शनल एसोसिएशन_प्रॉक्सी

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 problems और How can SQLAlchemy association_proxy be used bi-directionally? के जवाब का पालन करने की कोशिश की है, लेकिन वे मदद करने के लिए नहीं है:

अधिक वस्तुतः, निम्नलिखित (न्यूनतम-ish) प्रतिनिधि कोड स्निपेट अपने पिछले दावे विफल रहता है।

+6

धन्यवाद या काम करने वाले न्यूनतम उदाहरण के साथ एक अच्छा प्रश्न लिखना, +1 – enderland

उत्तर

2

मैंने your problem लगभग पूरी तरह दुर्घटना से खोजा, क्योंकि मैं यह पता लगाने की कोशिश कर रहा था कि क्या हो रहा है।

क्योंकि डीबी में कोई डेटा नहीं था, मैंने session.commit() जोड़ा। यह पता चला है कि (लिंक किए गए उत्तर से):

परिवर्तन डिस्क पर स्थायी रूप से नहीं बने रहते हैं, या अन्य लेन-देन के लिए दृश्यमान नहीं होते हैं जब तक कि डेटाबेस वर्तमान लेनदेन के लिए COMMIT प्राप्त नहीं करता है (जो session.commit है () कर देता है)।

क्योंकि आप परिवर्तनों में केवल .flush() हैं, sqlalchemy डेटाबेस को फिर से क्वेरी नहीं करता है। आप इसे जोड़कर सत्यापित कर सकते हैं:

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

और फिर बस अपना कोड चलाएं। यह उन सभी प्रश्नों को प्रदर्शित करेगा जो वे होते हैं। फिर आप session.flush() को session.commit() पर बदल सकते हैं और फिर पुन: चला सकते हैं, और आप देखेंगे कि कथन आपके commit के बाद चलाए जा रहे हैं।

ऐसा लगता है कि session.expire(user) या session.refresh(user) उपयोगकर्ता के रीफ्रेश को भी मजबूर करेगा। मुझे यकीन नहीं है कि इस बारे में स्पष्ट किए बिना अन्य ऑब्जेक्ट को प्रचारित करने के लिए अद्यतन को मजबूर करने का कोई तरीका है (या यदि यह भी वांछनीय है)।

+0

एक नज़र डालने के लिए धन्यवाद! आप सही हैं कि '.comit() 'ing इस समस्या को हल करता है। हालांकि, यह स्थिरता प्राप्त करने के लिए प्रतिबद्ध होने के लिए अवांछित लगता है। इसलिए यदि कोई बेहतर समाधान पोस्ट नहीं किया गया है तो मैं इस जवाब को एक दिन में स्वीकार कर दूंगा। – mickeyh

+1

@ मिकेह आप 'session.expire_all() 'जारी करके डेटा को पुनः लोड करने के लिए SQLAlchemy को मजबूर कर सकते हैं, लेकिन यह एक वर्कअराउंड जैसा लगता है। – univerio

+0

@ मिक्के या 'session.expire (उपयोगकर्ता)' या 'session.refresh (उपयोगकर्ता) 'स्पष्ट रूप से भी काम करता है। –

संबंधित मुद्दे