2013-10-14 5 views
9

में म्यूटेबल ट्रैकिंग के साथ पिकटाइप मेरे पास एक प्रोजेक्ट है जहां मैं एक रिलेशनल डीबी (पोस्टग्रेज़) में एक बड़ी संरचना (नेस्टेड ऑब्जेक्ट्स) स्टोर करना चाहता हूं। यह एक बड़ी संरचना का हिस्सा है और मुझे वास्तव में सीरियलाइजेशन प्रारूप की परवाह नहीं है - मुझे कॉलम में ब्लॉब होने में खुशी है - मैं बस इसे जारी रखने और इसे जल्दी से बहाल करने में सक्षम होना चाहता हूं।स्क्लेक्लेमी

मेरे उद्देश्यों के लिए SQLAlchemy PickleType ज्यादातर नौकरी करता है। मेरे पास यह मुद्दा है कि मैं गंदे चेक काम करना चाहता हूं (ऐसा कुछ जो म्यूटेबल प्रकारों के लिए उपयोग किया जाता है)। मैं उन्हें न केवल काम करना चाहता हूं अगर मैं पथों में जानकारी बदलता हूं लेकिन सीमाओं में भी (जो एक और स्तर नीचे बैठता है)।

class Group(Base): 
    __tablename__ = 'group' 

    id = Column(Integer, primary_key=True) 
    name = Column(String, nullable=False) 
    paths = Column(types.PickleType) 

class Path(object): 
    def __init__(self, style, bounds): 
     self.style = style 
     self.bounds = bounds 

class Bound(object): 
    def __init__(self, l, t, r, b): 
     self.l = l 
     self.t = t 
     self.r = r 
     self.b = b 

# this is all fine 
g = Group(name='g1', paths=[Path('blah', Bound(1,1,2,3)), 
          Path('other_style', Bound(1,1,2,3)),]) 
session.add(g) 
session.commit() 

# so is this 
g.name = 'g2' 
assert g in session.dirty 
session.commit() 

# but this won't work without some sort of tracking on the deeper objects 
g.paths[0].style = 'something else' 
assert g in session.dirty # nope 

मैंने इसे प्रबंधित करने की कोशिश कर रहे उत्परिवर्तनीय प्रकारों के साथ खेला है लेकिन कोई भाग्य नहीं है। कहीं और मैं एक जेसन कॉलम के लिए म्यूटेबल प्रकारों का उपयोग करता हूं जो ठीक है - एक तरह से जो सरल लगता है हालांकि इन वर्गों के साथ आपको वस्तुओं के भीतर वस्तुओं में परिवर्तनों को ट्रैक करने की आवश्यकता है।

किसी भी विचार की सराहना की।

उत्तर

5

सबसे पहले, जैसा कि आपको एहसास हुआ, आपको वस्तुओं के भीतर वस्तुओं में परिवर्तनों को ट्रैक करना होगा, क्योंकि SQLAlchemy को आंतरिक वस्तु को बदलने के लिए कोई रास्ता नहीं है। तो, हम एक आधार परिवर्तनशील वस्तु के साथ जिस तरह से हम दोनों के लिए उपयोग कर सकते हैं से बाहर है कि मिल जाएगा:

class MutableObject(Mutable, object): 
    @classmethod 
    def coerce(cls, key, value): 
     return value 

    def __getstate__(self): 
     d = self.__dict__.copy() 
     d.pop('_parents', None) 
     return d 

    def __setstate__(self, state): 
     self.__dict__ = state 

    def __setattr__(self, name, value): 
     object.__setattr__(self, name, value) 
     self.changed() 


class Path(MutableObject): 
    def __init__(self, style, bounds): 
     super(MutableObject, self).__init__() 
     self.style = style 
     self.bounds = bounds 


class Bound(MutableObject): 
    def __init__(self, l, t, r, b): 
     super(MutableObject, self).__init__() 
     self.l = l 
     self.t = t 
     self.r = r 
     self.b = b 

और हम भी पथ सूची में परिवर्तनों को ट्रैक करने की जरूरत है, इसलिए, हम उस करना है एक उत्परिवर्तनीय वस्तु भी। हालांकि, परिवर्तनशील() विधि को कॉल करते समय म्यूटेबल ट्रैक माता-पिता को प्रचारित करके बदलते हैं, और एसक्यूएलकेकी में वर्तमान कार्यान्वयन केवल एक अनुच्छेद के रूप में असाइन किए गए किसी व्यक्ति को एक अनुच्छेद के रूप में असाइन किया गया है, अनुक्रम की वस्तु के रूप में नहीं, एक शब्दकोश या एक सूची की तरह। यह वह जगह है जहां चीजें जटिल हो जाती हैं।

मुझे लगता है कि सूची आइटम में माता-पिता के रूप में सूची होनी चाहिए, लेकिन यह दो कारणों से काम नहीं करता है: पहला, _parents कमजोर एक कुंजी के लिए एक सूची नहीं ले सकता है, और दूसरा, बदले गए() संकेत शीर्ष पर सभी तरह से प्रचार नहीं करता है, इसलिए, हम केवल सूची को ही बदलकर चिह्नित करेंगे। मुझे 100% यकीन नहीं है कि यह कितना सही है, लेकिन जाने का तरीका सूची के माता-पिता को प्रत्येक आइटम पर असाइन करना प्रतीत होता है, इसलिए जब कोई आइटम बदल जाता है तो समूह ऑब्जेक्ट को flag_modified कॉल मिलता है। यह करना चाहिए।

class MutableList(Mutable, list): 
    @classmethod 
    def coerce(cls, key, value): 
     if not isinstance(value, MutableList): 
      if isinstance(value, list): 
       return MutableList(value) 
      value = Mutable.coerce(key, value) 

     return value   

    def __setitem__(self, key, value): 
     old_value = list.__getitem__(self, key) 
     for obj, key in self._parents.items(): 
      old_value._parents.pop(obj, None) 

     list.__setitem__(self, key, value) 
     for obj, key in self._parents.items(): 
      value._parents[obj] = key 

     self.changed() 

    def __getstate__(self): 
     return list(self) 

    def __setstate__(self, state): 
     self[:] = state 

हालांकि, यहां एक आखिरी मुद्दा है। माता-पिता को 'लोड' घटना को सुनने के लिए एक कॉल द्वारा असाइन किया जाता है, इसलिए प्रारंभ करने के समय, _parents dict खाली है, और बच्चों को कुछ भी सौंपा नहीं जाता है। मुझे लगता है कि हो सकता है कि लोड लोड ईवेंट को सुनकर आप कुछ क्लीनर तरीका भी कर सकें, लेकिन मुझे लगता है कि आइटम को पुनर्प्राप्त होने पर माता-पिता को पुन: असाइन करना होगा, इसलिए, इसे जोड़ें:

def __getitem__(self, key): 
     value = list.__getitem__(self, key) 

     for obj, key in self._parents.items(): 
      value._parents[obj] = key 

     return value 

अंत में, हम पर Group.paths कि MutableList उपयोग करने के लिए:

class Group(BaseModel): 
    __tablename__ = 'group' 

    id = db.Column(db.Integer, primary_key=True) 
    name = db.Column(db.String, nullable=False) 
    paths = db.Column(MutableList.as_mutable(types.PickleType)) 

और यह सब अपने परीक्षण कोड के साथ काम करना चाहिए:

g = Group(name='g1', paths=[Path('blah', Bound(1,1,2,3)), 
          Path('other_style', Bound(1,1,2,3)),]) 

session.add(g) 
db.session.commit() 

g.name = 'g2' 
assert g in db.session.dirty 
db.session.commit() 

g.paths[0].style = 'something else' 
assert g in db.session.dirty 

सच कहूं, मुझे यकीन है कि यह कैसे सुरक्षित है नहीं कर रहा हूँ जी के लिए यह उत्पादन पर नहीं है, और यदि आपको लचीली स्कीमा की आवश्यकता नहीं है, तो आप शायद पथ और बाउंड के लिए एक टेबल और रिश्तों का उपयोग करके बेहतर होंगे।

+0

शानदार उत्तर, सभी को काम करने के लिए समय निकालने के लिए धन्यवाद - मैं सराहना करता हूं कि यह एक बहुत ही गैर-मानक मामला है। यह सामान्य विचार है कि मुझे लगा कि यह कैसे काम करेगा लेकिन विवरण को समझ में नहीं आया।मुझे इसे वास्तव में समझने के लिए थोड़ा सा अध्ययन करना होगा - जैसा कि आप कहते हैं, शायद इस मार्ग पर जाने का अच्छा विचार भी नहीं है। सूची मुद्दे के आसपास काम करने का आपका विचार बहुत चालाक है, और मुझे यकीन नहीं है कि मैं हिट करता हूं। एक बार फिर धन्यवाद। जब मैं मोबाइल पर नहीं हूं तो आपको इस पर एक उपहार मिलेगा :) –

+0

धन्यवाद। मुझे मदद करने में खुशी है। –

+0

परेशान है कि मैं इस सवाल पर न्यूनतम बक्षीस दे सकता हूं क्योंकि अब मुझे यहां पर इनाम दिया गया था। मैंने पूरी तरह से असंबंधित प्रश्न पर एक बक्षीस शुरू कर दिया है कि मैं आपको 24 घंटे के समय में इनाम देने में सक्षम हूं। –

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