SQLAlchemy वर्जनिंग क्लास इंपोर्ट ऑर्डर की परवाह करता है

| | | | | | | | | |

मैं यहां गाइड का पालन कर रहा था:

http ://www.sqlalchemy.org/docs/orm/examples.html?highlight=versioning#versioned-objects

और एक समस्या का सामना करना पड़ा है। मैंने अपने संबंधों को परिभाषित किया है जैसे:

"मेरे मॉडल मॉड्यूल के आयात आदेश के बारे में परवाह नहीं है। यह सब सामान्य रूप से ठीक काम करता है, लेकिन जब मैं वर्जनिंग मेटा का उपयोग करता हूं तो मुझे निम्न त्रुटि मिलती है:

sqlalchemy.exc.InvalidRequestError: मैपर प्रारंभ करते समय Mapper|MyClass|stuffs, अभिव्यक्ति "व्यापारी" एक नाम का पता लगाने में विफल रहा ("नाम "MyClass" परिभाषित नहीं है")। यदि यह एक वर्ग का नाम है, तो दोनों आश्रित वर्गों को परिभाषित करने के बाद इस संबंध () को कक्षा में जोड़ने पर विचार करें। .

मैंने इस त्रुटि को ट्रैक किया:

 फ़ाइल "/home/nick/workspace/gm3/gm3/lib/history_meta.py", लाइन 90, __init__ मैपर = class_mapper(cls) फ़ाइल में "/home/nick/venv/tg2env/lib/python2.6/site-packages/sqlalchemy/orm/util.py", लाइन 622, class_mapper mapper = mapper.compile में () 

class VersionedMeta(DeclarativeMeta): def __init__(cls, cl) assname, bases, dict_): DeclarativeMeta.__init__(cls, classname, bases, dict_) try: mapper = class_mapper(cls) _history_mapper(mapper) UnmappedClassError को छोड़कर: पास 

मैंने समस्या ठीक कर दी कोशिश करके: एक लैम्ब्डा में सामान को छोड़कर और सभी आयात होने के बाद उन सभी को चलाना। यह काम करता है लेकिन थोड़ा बकवास लगता है, इसे ठीक करने का कोई तरीका बेहतर है?

धन्यवाद!

अपडेट करें

समस्या वास्तव में आयात आदेश के बारे में नहीं है। वर्जनिंग उदाहरण इस तरह से डिज़ाइन किया गया है कि मैपर को प्रत्येक वर्जन क्लास के कॉस्ट्रक्टर में संकलन की आवश्यकता होती है। और जब संबंधित वर्गों को अभी तक परिभाषित नहीं किया गया है तो संकलन विफल हो जाता है। सर्कुलर संबंधों के मामले में मैप किए गए वर्गों के परिभाषा क्रम को बदलकर इसे काम करने का कोई तरीका नहीं है।

अपडेट 2

जैसा कि उपरोक्त अद्यतन बताता है ( मुझे नहीं पता था कि आप यहां पर अन्य लोगों की पोस्ट संपादित कर सकते हैं :)) यह संभवतः परिपत्र संदर्भों के कारण है। किस मामले में कोई मेरे हैक को उपयोगी पाएगा (मैं इसे टर्बोगियर के साथ उपयोग कर रहा हूं) (संस्करण मेटा को बदलें और create_mappers Global in history_meta में जोड़ें)

create_mappers = [] class VersionedMeta(DeclarativeMeta) : def __init__(cls, classname, bases, dict_): DeclarativeMeta.__init__(cls, classname, bases, dict_) #मैंने इस कोड को जोड़ा क्योंकि यह क्रैश हो रहा था अन्यथा def make_mapper(): try: mapper = class_mapper(cls) _history_mapper (मैपर) UnmappedClassError को छोड़कर: create_mappers.append(lambda: make_mapper()) पास करें 

फिर आप अपने मॉडल __init__.py

< में निम्न जैसा कुछ कर सकते हैं कोड># यहां अपना मॉडल मॉड्यूल आयात करें। myproj.lib.history_meta से myproj.model.misc आयात से create_mappers आयात करें * myproj.model.actor से आयात करें * myproj.model.stuff1 से आयात करें * myproj.model.instrument से आयात करें * से myproj.model.stuff import * #setup the history [func() for func in create_mappers] 

इस तरह यह केवल मैपर बनाता है सभी वर्गों को परिभाषित करने के बाद।

अपडेट 3 थोड़ा असंबंधित लेकिन मुझे कुछ परिस्थितियों में एक डुप्लिकेट प्राथमिक कुंजी त्रुटि मिली (एक ही ऑब्जेक्ट में एक ही बार में 2 परिवर्तन करना) . मेरा समाधान एक नई प्राथमिक ऑटो-इन्क्रीमेंटिंग कुंजी जोड़ना रहा है। बेशक आपके पास MySQL के साथ 1 से अधिक नहीं हो सकते हैं, इसलिए मुझे इतिहास तालिका बनाने के लिए उपयोग की जाने वाली मौजूदा सामग्री को डी-प्राथमिक कुंजी देना पड़ा। मेरा समग्र कोड देखें (एक hist_id सहित और विदेशी कुंजी बाधा से छुटकारा पाने के लिए):

"""ऑफिकल sqlalchemy से चोरी """ sqlalchemy.ext.declarative से आयात DeclarativeMeta sqlalchemy.orm से आयात मैपर, class_mapper, विशेषताएँ, sqlalchemy.orm.exc से object_mapper आयात UnmappedClassError, sqlalchemy आयात तालिका से अनमैप्ड कॉलम त्रुटि, कॉलम, विदेशीकी कॉन्स्ट्रेन, sqlalchemy.orm.interfaces से इंटीजर sqlalchemy.orm.properties से सत्र एक्सटेंशन आयात करें sqlalchemy.types से संबंध प्रॉपर्टी आयात करें sqlalchemy.orm.session आयात से दिनांक समय आयात डेटाटाइम आयात करें। : col.foreign_keys में fk के लिए: यदि fk.references(table): रिटर्न ट्रू रिटर्न गलत def _history_mapper(local_mapper): cls = local_mapper.class_ # "active_history" फ्लैग # को कॉलम-मैप्ड पर सेट करें a ttributes ताकि local_mapper.iterate_properties में प्रोप के लिए जानकारी का पुराना संस्करण # हमेशा लोड (वर्तमान में इसे सभी विशेषताओं पर सेट करता है): getattr(local_mapper.class_, prop.key).impl.active_history = True super_mapper = local_mapper.inherits super_history_mapper = getattr (cls, "__history_mapper__", कोई नहीं) polymorphic_on = कोई नहीं super_fks = [] यदि नहीं तो super_mapper या local_mapper.local_table super_mapper.local_table नहीं है: cols = [] local_mapper.local_table.c में कॉलम के लिए: यदि column.name == "संस्करण": जारी रखें col = column.copy() col.unique = False #don"t col.autoincrement यदि col.autoincrement: col.autoincrement = False #sqllite ऑटो इंक्रीमेंट कीज़ के साथ आ जाता है यदि हमारे पास एक है समग्र कुंजी अगर col.primary_key: col.primary_key = गलत अगर super_mapper और col_references_table(column, super_mapper.local_table): super_fks.append((col.key, list(super_history_mapper.base_mapper.local_table.primary_key)[0])) cols। संलग्न करें (कॉल) यदि कॉलम local_mapper.polymorphi . है c_on: polymorphic_on = col #if super_mapper: # super_fks.append(("version", super_history_mapper.base_mapper.local_table.c.version)) cols.append(Column("hist_id", Integer, Primary_key=True, autoincrement=True) ) cols.append(Column("version", Integer)) cols.append(Column("changed", DateTime, default=datetime.datetime.now)) अगर super_fks: cols.append(ForeignKeyConstraint(*zip(*super_fks) )) टेबल = टेबल (लोकल_मैपर.लोकल_टेबल.नाम + "_हिस्ट्री", लोकल_मैपर.लोकल_टेबल.मेटाडेटा, *cols, mysql_engine = "InnoDB") और: # सिंगल टेबल इनहेरिटेंस। कोई भी अतिरिक्त कॉलम लें जो # जोड़े गए हों और उन्हें इतिहास तालिका में जोड़ें। local_mapper.local_table.c में कॉलम के लिए: यदि column.key super_history_mapper.local_table.c में नहीं है: col = column.copy() super_history_mapper.local_table.append_column(col) table = कोई नहीं अगर super_history_mapper: bases = (super_history_mapper.class_,) अन्य: कुर्सियां = local_mapper.base_mapper.class_.__bases__ versioned_cls = type.__new__(type, "%sHistory" % cls.__name__, bases, {}) m = मैपर (versioned_cls, टेबल, इनहेरिट्स = सुपर_हिस्ट्री_मैपर, पॉलीमॉर्फिक_ऑन = पॉलीमॉर्फिक_ऑन, पॉलीमॉर्फिक_ऑन, पॉलीमॉर्फिक_आइडेंटिटी =local_mapper.polymorphic_identity ) cls.__history_mapper__ = m यदि नहीं तो super_history_mapper: cls.version = Column("version", Integer, default=1, nullable=False) create_mappers = [] class VersionedMeta(DeclarativeMeta): def __init__(cls, classname) , आधार, dict_): DeclarativeMeta.__init__(cls, classname, bases, dict_) #मैंने इस कोड को जोड़ा क्योंकि यह क्रैश हो रहा था अन्यथा def make_mapper(): try: mapper = class_mapper(cls) _history_mapper(mapper) UnmappedClassError को छोड़कर: पास create_mappers.append (लैम्ब्डा: make_mapper ()) def versioned_objects (iter): इटर में obj के लिए: if hasattr(obj, "__history_mapper__"): उपज obj def create_version(obj, session, delete = False): obj_mapper = object_mapper(obj) history_mapper = obj.__history_mapper__ history_cls = history_mapper.class_ obj_state = विशेषताएँ.instance_state (obj) attr = {} obj_changed = ओम के लिए गलत, ज़िप में HM (obj_mapper.iterate_to_root (), history_mapper.iterate_to_root ()): यदि hm.single: hm में hist_col के लिए जारी रखें .local_table.c: यदि hist_col.key == "संस्करण" या hist_col.key == "बदला हुआ" या hist_col.key == "hist_id": जारी रखें obj_col = om.local_table.c [hist_col.key] # मान प्राप्त करें # मैप किए गए कॉलम से संबंधित मैपरप्रॉपर्टी पर आधारित # विशेषता का। यह मैपरप्रॉपर्टीज # के उपयोग की अनुमति देगा जिसमें मैप किए गए कॉलम की तुलना में एक अलग कुंजीनाम है। कोशिश करें: प्रॉप = obj_mapper.get_property_by_column(obj_col) UnmappedColumnError को छोड़कर: # सिंगल टेबल इनहेरिटेंस के मामले में, मैप की गई टेबल पर # कॉलम हो सकते हैं, जो केवल सबक्लास के लिए है। # बेस क्लास पर सबक्लास कॉलम की "अनमैप्ड" स्थिति sql 0.5.2 के रूप में घोषणात्मक मॉड्यूल की एक विशेषता है। जारी रखें # समय सीमा समाप्त वस्तु विशेषताएँ और आस्थगित कॉल भी # तानाशाही में नहीं हो सकते हैं। getattr() का उपयोग करके कोई फर्क नहीं पड़ता कि इसे लोड करने के लिए मजबूर करें। अगर प्रोप.की obj_state.dict में नहीं है: getattr(obj, prop.key) a, u, d = properties.get_history(obj, prop.key) अगर d: attr[hist_col.key] = d[0] obj_changed = ट्रू एलिफ़ यू: attr[hist_col.key] = u[0] अन्य: # यदि विशेषता का कोई मूल्य नहीं था। attr[hist_col.key] = a[0] obj_changed = True अगर नहीं तो obj_changed: # नहीं बदला, लेकिन हमारे बीच संबंध हैं। ठीक है # उन्हें भी obj_mapper.iterate_properties में प्रोप के लिए जांचें: यदि isinstance (prop, रिलेशनशिपप्रॉपर्टी) और विशेषताएँ। get_history (obj, prop.key).has_changes (): obj_changed = ट्रू ब्रेक अगर obj_changed नहीं है और हटाया नहीं गया है: वापसी attr [" version"] = obj.version hist = history_cls() कुंजी के लिए, attr.iteritems() में मान: setattr(hist, key, value) obj.version += 1 session.add(hist) class VersionedListener(SessionExtension): def पहले_फ्लश (स्वयं, सत्र, फ्लश_कॉन्टेक्स्ट, उदाहरण): वर्जन_ऑब्जेक्ट्स (सत्र। गंदे) में ओबीजे के लिए: वर्जन_ऑब्जेक्ट्स (सत्र। हटाए गए) में ओबीजे के लिए create_version (ओबीजे, सत्र): create_version (obj, सत्र, हटाया गया = सत्य)