2012-10-08 13 views
8

मेरे पास दो टेबल हैं, ए और बी दोनों का प्राथमिक कुंजी आईडी है। उनके पास कई से अधिक रिश्ते हैं, एसईसी।स्क्लेल्चेमी: माध्यमिक संबंध अपडेट

SEC = Table('sec', Base.metadata, 
    Column('a_id', Integer, ForeignKey('A.id'), primary_key=True, nullable=False), 
    Column('b_id', Integer, ForeignKey('B.id'), primary_key=True, nullable=False) 
) 

class A(): 
    ... 
    id = Column(Integer, primary_key=True) 
    ... 
    rels = relationship(B, secondary=SEC) 

class B(): 
    ... 
    id = Column(Integer, primary_key=True) 
    ... 

चलिए कोड के इस टुकड़े पर विचार करें।

a = A() 
b1 = B() 
b2 = B() 
a.rels = [b1, b2] 
... 
#some place later 
b3 = B() 
a.rels = [b1, b3] # errors sometimes 

कभी कभी, मैं अंतिम पंक्ति

duplicate key value violates unique constraint a_b_pkey 

मेरी समझ में कह पर कोई त्रुटि मिलती है, मैं इसे फिर से 'सेकंड' तालिका में (a.id, b.id) जोड़ने के लिए कोशिश करता है लगता है जिसके परिणामस्वरूप एक अद्वितीय बाधा त्रुटि होती है। क्या यही वह चीज है? यदि हां, तो मैं इससे कैसे बच सकता हूं? यदि नहीं, तो मुझे यह त्रुटि क्यों है?

उत्तर

3

आपके द्वारा उल्लिखित त्रुटि वास्तव में सेकंड तालिका में एक विवादित मूल्य डालने से है। यह सुनिश्चित करने के लिए कि यह ऑपरेशन से है, आपको लगता है कि यह कुछ पिछला परिवर्तन नहीं है, एसक्यूएल लॉगिंग चालू करें और जांचें कि त्रुटि से पहले डालने का प्रयास करने वाले मूल्य क्या हैं।

कई से अधिक संग्रह मूल्य को ओवरराइट करते समय, एसक्लाक्लेमी डेटाबेस में राज्य के साथ संग्रह की नई सामग्री की तुलना करता है और संगत मुद्दों को हटाने और बयान सम्मिलित करता है। जब तक आप स्क्लेक्लेमी आंतरिक में चारों ओर पोकिंग नहीं कर रहे हैं, तब तक इस त्रुटि का सामना करने के दो तरीके हो सकते हैं।

पहला समवर्ती संशोधन है: प्रक्रिया 1 मूल्य a.rels लाता है और नोटिस करता है कि यह खाली है, इस बीच प्रक्रिया 2 भी a.rels लाता है, इसे [बी 1, बी 2] पर सेट करता है और (ए, बी 1) , (ए, बी 2) tuples, प्रक्रिया 1 [b1, b3] को a.rels सेट करता है यह देखते हुए कि पिछली सामग्री खाली थी और जब यह सेक tuple (ए, बी 1) फ्लश करने की कोशिश करता है तो यह एक डुप्लिकेट कुंजी त्रुटि प्राप्त करता है। ऐसे मामलों में सही कार्रवाई आमतौर पर लेनदेन को शीर्ष से पुनः प्राप्त करने के लिए होती है। आप इस मामले में एक क्रमबद्ध त्रुटि प्राप्त करने के लिए serializable transaction isolation का उपयोग कर सकते हैं जो डुप्लिकेट कुंजी त्रुटि उत्पन्न करने वाले व्यावसायिक तर्क त्रुटि से अलग है।

दूसरा मामला तब होता है जब आप SQLAlchemy को मनाने में कामयाब रहे हैं कि आपको noload पर रिल्स विशेषता की लोडिंग रणनीति सेट करके डेटाबेस स्थिति जानने की आवश्यकता नहीं है। यह lazy='noload' पैरामीटर जोड़कर, या पूछताछ करते समय रिश्ते को परिभाषित करते समय किया जा सकता है, क्वेरी पर .options(noload(A.rels)) पर कॉल करना। SQLAlchemy मान लेंगे कि इस तालिका के साथ लोड की गई वस्तुओं के लिए सेक तालिका में कोई मिलान पंक्ति नहीं है।

+0

मुझे सच में यकीन नहीं है क्यों।मुझे इसे ठीक से जांचने की ज़रूरत है और आपको बताएगी। सहायता के लिए धन्यवाद। – Sri

8

समस्या यह है कि आप यह सुनिश्चित करना चाहते हैं कि आपके द्वारा बनाए गए उदाहरण अद्वितीय हैं। हम एक वैकल्पिक कन्स्ट्रक्टर बना सकते हैं जो मौजूदा अवांछित उदाहरणों का कैश जांचता है या एक नया उदाहरण लौटने से पहले मौजूदा प्रतिबद्ध उदाहरण के लिए डेटाबेस से पूछताछ करता है।

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

engine = create_engine('sqlite:///:memory:', echo=True) 
Session = sessionmaker(engine) 
Base = declarative_base(engine) 

session = Session() 


class Role(Base): 
    __tablename__ = 'role' 

    id = Column(Integer, primary_key=True) 
    name = Column(String, nullable=False, unique=True) 

    @classmethod 
    def get_unique(cls, name): 
     # get the session cache, creating it if necessary 
     cache = session._unique_cache = getattr(session, '_unique_cache', {}) 
     # create a key for memoizing 
     key = (cls, name) 
     # check the cache first 
     o = cache.get(key) 
     if o is None: 
      # check the database if it's not in the cache 
      o = session.query(cls).filter_by(name=name).first() 
      if o is None: 
       # create a new one if it's not in the database 
       o = cls(name=name) 
       session.add(o) 
      # update the cache 
      cache[key] = o 
     return o 


Base.metadata.create_all() 

# demonstrate cache check 
r1 = Role.get_unique('admin') # this is new 
r2 = Role.get_unique('admin') # from cache 
session.commit() # doesn't fail 

# demonstrate database check 
r1 = Role.get_unique('mod') # this is new 
session.commit() 
session._unique_cache.clear() # empty cache 
r2 = Role.get_unique('mod') # from database 
session.commit() # nop 

# show final state 
print session.query(Role).all() # two unique instances from four create calls 

create_unique विधि example from the SQLAlchemy wiki से प्रेरित था:

यहाँ ऐसी पद्धति का एक प्रदर्शन है। यह संस्करण लचीलापन पर सादगी का पक्ष लेते हुए बहुत कम संकुचित है। मैंने इसे बिना किसी समस्या के उत्पादन प्रणालियों में उपयोग किया है।

स्पष्ट रूप से सुधार किए जा सकते हैं; यह सिर्फ एक साधारण उदाहरण है। get_unique विधि किसी भी मॉडल के लिए उपयोग किए जाने के लिए UniqueMixin से विरासत में प्राप्त की जा सकती है। तर्कों के अधिक लचीला ज्ञापन लागू किया जा सकता है। यह चींटियों एजामा द्वारा वर्णित विवादित डेटा डालने वाले कई धागे की समस्या को भी अलग करता है; हैंडलिंग जो अधिक जटिल है लेकिन एक स्पष्ट विस्तार होना चाहिए। मैं तुम्हें छोड़ देता हूँ।

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