2011-08-18 16 views
12

मेरे पास Test मॉडल/टेबल और TestAuditLog मॉडल/टेबल है, जो एसक्यूएलकेमी और एसक्यूएल सर्वर 2008 का उपयोग कर रहा है। दोनों के बीच संबंध Test.id == TestAuditLog.entityId है, जिसमें एक परीक्षण के साथ कई लेखापरीक्षा होती है लॉग। TestAuditLogTest तालिका में पंक्तियों में परिवर्तनों का इतिहास रखने का इरादा है। मैं ट्रैक करना चाहता हूं कि Test हटा दिया गया है, लेकिन मुझे इसके साथ परेशानी हो रही है। SQL सर्वर प्रबंधन स्टूडियो में, मैंने FK_TEST_AUDIT_LOG_TEST रिश्ते की "Enforce Foreign Key Constraint" संपत्ति को "नहीं" पर सेट किया है, यह सोचकर कि TestAuditLog पंक्ति entityId के साथ मौजूद होने की अनुमति देगी जो अब Test.id से कनेक्ट नहीं है क्योंकि Test हटा दी गयी है। हालांकि, जब मैं SQLAlchemy के साथ एक TestAuditLog बनाने का प्रयास करें और फिर Test हटाने के लिए, मैं एक त्रुटि मिलती है:SQLAlchemy - रिश्ते पर विदेशी कुंजी बाधा को लागू न करें

(IntegrityError) ('23000', "[23000] [Microsoft][ODBC SQL Server Driver][SQL Server]Cannot insert the value NULL into column 'AL_TEST_ID', table 'TEST_AUDIT_LOG'; column does not allow nulls. UPDATE fails. (515) (SQLExecDirectW); [01000] [Microsoft][ODBC SQL Server Driver][SQL Server]The statement has been terminated. (3621)") u'UPDATE [TEST_AUDIT_LOG] SET [AL_TEST_ID]=? WHERE [TEST_AUDIT_LOG].[AL_ID] = ?' (None, 8)

मैं, Test और TestAuditLog के बीच विदेशी कुंजी संबंधों की वजह से लगता है के बाद मैं Test पंक्ति को हटा, SQLAlchemy NULLentityId रखने के लिए उस परीक्षण के ऑडिट लॉग को अपडेट करने का प्रयास कर रहा है। मैं यह नहीं करना चाहता हूं; मैं चाहता हूं कि स्क्लेक्लेमी अकेले ऑडिट लॉग छोड़ दें। ऑडिट लॉग मौजूद होने की अनुमति देने के लिए मैं स्क्लाक्लेमी को कैसे बता सकता हूं जिसका entityId किसी भी Test.id से कनेक्ट नहीं है?

मैं सिर्फ अपनी टेबल से ForeignKey को दूर करने की कोशिश की, लेकिन मैं अभी भी myTest.audits कहना और एक परीक्षण के ऑडिट लॉग के सभी प्राप्त करने में सक्षम होना चाहते हैं, और SQLAlchemy Test और TestAuditLog में शामिल होने के लिए कैसे नहीं जानते हुए भी बारे में शिकायत की। जब मैंने relationship पर primaryjoin निर्दिष्ट किया, तो यह कॉलम के साथ ForeignKey या ForeignKeyConstraint नहीं होने के बारे में चिंतित था।

class TestAuditLog(Base, Common): 
    __tablename__ = u'TEST_AUDIT_LOG' 
    entityId = Column(u'AL_TEST_ID', INTEGER(), ForeignKey(u'TEST.TS_TEST_ID'), 
     nullable=False) 
    ... 

class Test(Base, Common): 
    __tablename__ = u'TEST' 
    id = Column(u'TS_TEST_ID', INTEGER(), primary_key=True, nullable=False) 
    audits = relationship(TestAuditLog, backref="test") 
    ... 

और यहाँ इसकी ऑडिट लॉग रखते हुए मैं कैसे एक परीक्षण नष्ट करने के लिए कोशिश कर रहा हूँ है, उनके entityId बरकरार:

test = Session.query(Test).first() 
    Session.begin() 
    try: 
     Session.add(TestAuditLog(entityId=test.id)) 
     Session.flush() 
     Session.delete(test) 
     Session.commit() 
    except: 
     Session.rollback() 
     raise 

उत्तर

11

आपके द्वारा इस हल कर सकते हैं

यहाँ मेरी मॉडल हैं:

  • सूत्री-1: एकनहीं होने 0 न RDBMS स्तर पर और न ही एसए स्तर पर
  • सूत्री -2: स्पष्ट रूप से संबंध के लिए शर्तों में शामिल होने निर्दिष्ट
  • सूत्री-3: निशान संबंध cascades passive_deletes ध्वज पर भरोसा करने के

from sqlalchemy import create_engine, Column, Integer, String, ForeignKey 
from sqlalchemy.orm import scoped_session, sessionmaker, relationship 
from sqlalchemy.ext.declarative import declarative_base 

Base = declarative_base() 
engine = create_engine('sqlite:///:memory:', echo=False) 

Session = sessionmaker(bind=engine) 

class TestAuditLog(Base): 
    __tablename__ = 'TEST_AUDIT_LOG' 
    id = Column(Integer, primary_key=True) 
    comment = Column(String) 

    entityId = Column('TEST_AUDIT_LOG', Integer, nullable=False, 
        # POINT-1 
        #ForeignKey('TEST.TS_TEST_ID', ondelete="CASCADE"), 
        ) 

    def __init__(self, comment): 
     self.comment = comment 

    def __repr__(self): 
     return "<TestAuditLog(id=%s entityId=%s, comment=%s)>" % (self.id, self.entityId, self.comment) 

class Test(Base): 
    __tablename__ = 'TEST' 
    id = Column('TS_TEST_ID', Integer, primary_key=True) 
    name = Column(String) 

    audits = relationship(TestAuditLog, backref='test', 
       # POINT-2 
       primaryjoin="Test.id==TestAuditLog.entityId", 
       foreign_keys=[TestAuditLog.__table__.c.TEST_AUDIT_LOG], 
       # POINT-3 
       passive_deletes='all', 
      ) 

    def __init__(self, name): 
     self.name = name 

    def __repr__(self): 
     return "<Test(id=%s, name=%s)>" % (self.id, self.name) 


Base.metadata.create_all(engine) 

################### 
## tests 
session = Session() 

# create test data 
tests = [Test("test-" + str(i)) for i in range(3)] 
_cnt = 0 
for _t in tests: 
    for __ in range(2): 
     _t.audits.append(TestAuditLog("comment-" + str(_cnt))) 
     _cnt += 1 
session.add_all(tests) 
session.commit() 
session.expunge_all() 
print '-'*80 

# check test data, delete one Test 
t1 = session.query(Test).get(1) 
print "t: ", t1 
print "t.a: ", t1.audits 
session.delete(t1) 
session.commit() 
session.expunge_all() 
print '-'*80 

# check that audits are still in the DB for deleted Test 
t1 = session.query(Test).get(1) 
assert t1 is None 
_q = session.query(TestAuditLog).filter(TestAuditLog.entityId == 1) 
_r = _q.all() 
assert len(_r) == 2 
for _a in _r: 
    print _a 
: पूरी तरह से नीचे काम कर कोड का टुकड़ा आप एक विचार ( अंक code में हाइलाइट किया जाता) देना चाहिए

एक और विकल्प एफके में उपयोग किए गए कॉलम को डुप्लिकेट करना होगा, और एफके कॉलम को ON CASCADE SET NULL विकल्प के साथ शून्य करना होगा। इस तरह आप अभी भी इस कॉलम का उपयोग कर हटाए गए ऑब्जेक्ट्स के ऑडिट ट्रेल की जांच कर सकते हैं।

+0

'रिश्तेदार' पर 'passive_deletes = 'all'' ने किया था!इस तरह मैं रिश्तों को रखने में सक्षम था और स्क्लेक्लेमी वापस नहीं गया और 'test' हटाने पर' entityId' को मिटा देने का प्रयास करें। धन्यवाद! –

+0

बस संदर्भ के लिए - इसे रिश्ते के मूल पक्ष पर 'lazy =" गतिशील "भी सेट करना होगा, इसलिए जब आपको इसकी आवश्यकता न हो तो sqlalchemy सभी बच्चों को नहीं लाएगा (यानी जब मूल तालिका में केवल अप्रासंगिक फ़ील्ड अपडेट हो)। – Greg0ry

+0

@ ग्रेग 0: नहीं, आपको इसकी आवश्यकता नहीं है। जैसा कि [लोडर रणनीतियों का उपयोग करना: आलसी लोडिंग, उत्सुक लोडिंग] (http://docs.sqlalchemy.org/en/rel_1_0/orm/loading_relationships.html#using-loader-strategies-lazy-loading-eager-loading) में प्रलेखित के रूप में: * डिफ़ॉल्ट रूप से, सभी अंतर-वस्तु संबंध आलसी लोडिंग हैं ... *। इसलिए जब तक आप अन्यथा नहीं करते हैं, तब तक माता-पिता को तब तक बच्चों को लोड नहीं करना चाहिए जब तक कि आप उन्हें एक्सेस न करें। – van

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