2010-06-06 2 views
9

में प्रतिलिपि और गहरी प्रतिलिपि में सहायता करें मुझे लगता है कि मैंने my previous question में बहुत अधिक मांगने की कोशिश की है, इसलिए इसके लिए क्षमा करें। मुझे अपनी स्थिति को इस तरह से सरल तरीके से प्रस्तुत करने दें जैसे मैं कर सकता हूं।पायथन

असल में, मुझे शब्दकोशों का एक समूह मिला है जो मेरी वस्तुओं का संदर्भ देता है, जो बदले में एसक्यूएलकेमी का उपयोग करके मैप किए जाते हैं। मेरे साथ सब ठीक है। हालांकि, मैं उन शब्दकोशों की सामग्री में पुनरावृत्ति परिवर्तन करना चाहता हूं। समस्या यह है कि ऐसा करने से वे संदर्भित वस्तुओं को बदल देंगे --- और copy.copy() का उपयोग करना अच्छा नहीं है क्योंकि यह केवल शब्दकोश के भीतर निहित संदर्भों की प्रतिलिपि बनाता है। इस प्रकार, अगर मैंने कुछ कॉपी किया है, तो जब मैं कोशिश करता हूं, तो print शब्दकोष की सामग्री कहें, मुझे केवल ऑब्जेक्ट के लिए नवीनतम अपडेट किए गए मान मिलेंगे।

यही कारण है कि मैं copy.deepcopy() का उपयोग करना चाहता था लेकिन यह SQLAlchemy के साथ काम नहीं करता है। अब मैं एक दुविधा में हूं क्योंकि मुझे पुनरावृत्ति परिवर्तन करने से पहले अपने ऑब्जेक्ट के कुछ विशेषताओं की प्रतिलिपि बनाने की आवश्यकता है।

सारांश में, मैं एक ही समय में SQLAlchemy और उपयोग करने के लिए जब परिवर्तन करने मैं अपने वस्तु की एक प्रति हो सकता है यकीन है कि जिम्मेदार बताते हैं बनाने के तो मैं संदर्भित वस्तु ही बदल नहीं है की जरूरत है।

कोई सलाह, सहायता, सुझाव इत्यादि?


Edit: कुछ कोड जोड़ा है।

class Student(object): 
    def __init__(self, sid, name, allocated_proj_ref, allocated_rank): 
     self.sid = sid 
     self.name = name 
     self.allocated_proj_ref = None 
     self.allocated_rank = None 

students_table = Table('studs', metadata, 
    Column('sid', Integer, primary_key=True), 
    Column('name', String), 
    Column('allocated_proj_ref', Integer, ForeignKey('projs.proj_id')), 
    Column('allocated_rank', Integer) 
) 

mapper(Student, students_table, properties={'proj' : relation(Project)}) 

students = {} 

students[sid] = Student(sid, name, allocated_project, allocated_rank) 

इस प्रकार, गुण है कि मैं बदलने वाला है allocated_proj_ref और allocated_rank विशेषताएँ हैं। students_table अद्वितीय छात्र आईडी (sid) का उपयोग करके कुंजी है।


Question

मैं गुण मैं ऊपर बदल बनाए रखना चाहते हैं - मेरा मतलब है, कि मूल रूप से कारण है कि मैं SQLA उपयोग करने का फैसला है। हालांकि, मैप किए गए ऑब्जेक्ट को बदल दिया जाएगा, जिसकी अनुशंसा नहीं की जाती है। इस प्रकार, यदि मैं doppelgänger में परिवर्तन करता हूं, ऑब्जेक्ट को अनैप्ड किया गया ... क्या मैं उन परिवर्तनों को ले सकता हूं और मैप ऑब्जेक्ट के लिए फ़ील्ड/तालिका अपडेट कर सकता हूं।

एक अर्थ में मैं डेविड के secondary solution का अनुसरण कर रहा हूं जहां मैं कक्षा का एक और संस्करण बना रहा हूं जो मैप नहीं किया गया है।


मैं StudentDBRecord समाधान नीचे उल्लेख उपयोग करने की कोशिश लेकिन एक त्रुटि मिल गया है!

File "Main.py", line 25, in <module> 
    prefsTableFile = 'Database/prefs-table.txt') 
File "/XXXX/DataReader.py", line 158, in readData 
readProjectsFile(projectsFile) 
File "/XXXX/DataReader.py", line 66, in readProjectsFile 
supervisors[ee_id] = Supervisor(ee_id, name, original_quota, loading_limit) 
File "<string>", line 4, in __init__ 
raise exc.UnmappedClassError(class_) 
sqlalchemy.orm.exc.UnmappedClassError: Class 'ProjectParties.Student' is not mapped 

इसका मतलब यह है कि Student मैप किया जाना चाहिए?


Health warning!

किसी ने यहाँ एक बहुत अच्छी अतिरिक्त मुद्दा बताया। देखें, भले ही मैं copy.deepcopy() को गैर-मैप किए गए ऑब्जेक्ट पर कॉल कर रहा हूं, इस मामले में, मान लीजिए कि यह ऊपर वर्णित छात्रों के शब्दकोश है, गहरी प्रतिलिपि की प्रतिलिपि बनाता है।मेरे allocated_proj_ref वास्तव में एक Project वस्तु है, और मुझे लगता है कि के लिए एक इसी projects शब्दकोश मिल गया है।

तो मैं students और projects दोनों deepcopy - जो मैं कर रहा हूँ - वे कहते हैं मैं मामलों होगा जहां students के allocated_proj_ref विशेषता projects शब्दकोश में उदाहरणों के साथ मिलान के साथ मुद्दों होगा।

इस प्रकार, मैं इसे ले कि मैं फिर से परिभाषित करने/ओवरराइड (है कि यह क्या कहते हैं यह है ना?) def __deecopy__(self, memo): या ऐसा ही कुछ का उपयोग कर प्रत्येक कक्षा में deepcopy होगा?


मैं मैं ओवरराइड यह सब SQLA सामान (जो <class 'sqlalchemy.util.symbol'> और <class 'sqlalchemy.orm.state.InstanceState'> हैं), लेकिन सब कुछ है कि एक मैप किया वर्ग का हिस्सा है नकल पर ध्यान नहीं देता __deepcopy__ ऐसी है कि करना चाहते हैं चाहते हैं।

कोई सुझाव, कृपया?

+0

जब आप मैप किए गए वस्तुओं को संशोधित, आप मूल या परिवर्तित संस्करणों चाहते हो कायम किया जाना है: यहाँ अवधारणा कोड नमूने की साबित होता है? –

+0

@ विंस्टन: या तो मूल बने रहें या एक विशिष्ट परिवर्तित संस्करण ('सर्वश्रेष्ठ' - संपादन देखें) बने रहें। मैं पहले से ही बाद में समस्याओं को देख सकता हूं क्योंकि 'सर्वश्रेष्ठ' अभी भी नहीं रह रहा है क्योंकि मैं ऑब्जेक्ट को अपडेट कर रहा हूं। आह। – PizzAzzra

+0

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

उत्तर

1

अगर मैं याद कर रहा हूँ/सही ढंग से सोच, SQLAlchemy में आप सामान्य रूप से एक समय है कि किसी दिए गए डेटाबेस रिकॉर्ड से मेल खाती है में केवल एक ही वस्तु है। यह किया जाता है ताकि SQLAlchemy डेटाबेस के साथ सिंक में अपने अजगर वस्तुओं रख सकते हैं, और इसके विपरीत (अच्छी तरह से, नहीं अगर वहाँ अजगर बाहर से समवर्ती डीबी उत्परिवर्तन हैं, लेकिन यह एक और कहानी है)। तो समस्या यह है कि, यदि आप इन मैप किए गए ऑब्जेक्ट्स में से किसी एक को प्रतिलिपि बनाना चाहते हैं, तो आप दो अलग-अलग ऑब्जेक्ट्स के साथ हवादार होंगे जो समान डेटाबेस रिकॉर्ड से मेल खाते हैं। यदि आप एक बदलते हैं, तो उनके पास अलग-अलग मान होंगे, और डेटाबेस दोनों एक ही समय में मेल नहीं खा सकता है।

मैं आप तय है कि क्या आप डेटाबेस रिकॉर्ड परिवर्तनों को जब आप अपने प्रति की एक विशेषता परिवर्तन को दिखाने के लिए चाहते करने के लिए आवश्यकता हो सकती है क्या सोचते हैं। यदि ऐसा है, तो आपको ऑब्जेक्ट्स को कॉपी नहीं करना चाहिए, आपको केवल उसी उदाहरण का पुन: उपयोग करना चाहिए।

दूसरी ओर, यदि आप प्रतिलिपि अद्यतन करते समय मूल डेटाबेस रिकॉर्ड को बदलना नहीं चाहते हैं, तो आपके पास एक और विकल्प है: क्या प्रतिलिपि डेटाबेस में एक नई पंक्ति बननी चाहिए? या इसे डेटाबेस रिकॉर्ड में मैप नहीं किया जाना चाहिए? पूर्व मामले में, आप एक ही कक्षा का एक नया उदाहरण बनाकर और मूल्यों पर प्रतिलिपि बनाकर कॉपी ऑपरेशन को कार्यान्वित कर सकते हैं, वैसे ही आपने मूल ऑब्जेक्ट बनाया है। यह शायद आपके SQLAlchemy मैप किए गए वर्ग की __deepcopy__() विधि में किया जाएगा। बाद के मामले में (कोई मानचित्रण नहीं), आपको एक अलग वर्ग की आवश्यकता होगी जिसमें सभी समान फ़ील्ड हों लेकिन SQLAlchemy का उपयोग करके मैप नहीं किया गया है। असल में, यह संभवतया अधिक समझ में आ जाएगा कि आपकी एसक्लाक्लेमी-मैपड क्लास इस गैर-मैप किए गए वर्ग का उप-वर्ग है, और केवल उप-वर्ग के लिए मैपिंग करें।

संपादित: ठीक है, स्पष्ट करने के लिए क्या मुझे लगता है कि अंतिम बिंदु का मतलब: अभी आप एक Student वर्ग है कि अपने छात्रों को प्रतिनिधित्व करने के लिए प्रयोग किया जाता है है।

class Student(object): 
    def __init__(self, sid, name, allocated_proj_ref, allocated_rank): 
     self.sid = sid 
     self.name = name 
     self.allocated_project = None 
     self.allocated_rank = None 

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

class StudentDBRecord(Student): 
    def __init__(self, student): 
     super(StudentDBRecord, self).__init__(student.sid, student.name, 
      student.allocated_proj_ref, student.allocated_rank) 

# this call remains the same 
students_table = Table('studs', metadata, 
    Column('sid', Integer, primary_key=True), 
    Column('name', String), 
    Column('allocated_proj_ref', Integer, ForeignKey('projs.proj_id')), 
    Column('allocated_rank', Integer) 
) 

# this changes 
mapper(StudentDBRecord, students_table, properties={'proj' : relation(Project)}) 

अब आप Student के उदाहरण है, जो तुच्छ हैं का उपयोग कर अपने अनुकूलन एल्गोरिथ्म लागू करेगा - तो Student वस्तुओं की विशेषताएं परिवर्तित रूप में, कुछ भी नहीं डेटाबेस के लिए होता है। इसका मतलब है कि आप सुरक्षित रूप से copy या deepcopy सुरक्षित रूप से उपयोग कर सकते हैं।जब आप पूर्ण कर लिया हैं, तो आप Student उदाहरणों StudentDBRecord उदाहरणों के लिए, कुछ

students = ...dict with best solution... 
student_records = [StudentDBRecord(s) for s in students.itervalues()] 
session.commit() 

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

EDIT 2: तो हो सकता है कि यह काम न करे। एक त्वरित फिक्स Student कन्स्ट्रक्टर StudentDBRecord में प्रतिलिपि बनाना होगा और StudentDBRecord इसके बजाय object बढ़ाएं।

class StudentDBRecord(object): 
    def __init__(self, student): 
     self.sid = student.sid 
     self.name = student.name 
     self.allocated_project = student.allocated_project 
     self.allocated_rank = student.allocated_rank 

या आप यह सामान्यीकरण करना चाहते थे: जो है, इस के साथ StudentDBRecord के पिछले परिभाषा को बदलने के

class StudentDBRecord(object): 
    def __init__(self, student): 
     for attr in dir(student): 
      if not attr.startswith('__'): 
       setattr(self, attr, getattr(student, attr)) 

यह बाद परिभाषा को Student के सभी गैर-विशेष गुण पर कॉपी जाएगा StudentDBRecord

+0

@ डेविड: बस मेरे प्रश्न को कक्षाओं में से एक के उदाहरण के साथ अपडेट किया गया। "... यदि आप प्रतिलिपि अद्यतन करते समय मूल डेटाबेस रिकॉर्ड को बदलना नहीं चाहते हैं" - यही वह है जिसे मैं ढूंढ रहा हूं। क्या आप वर्णन कर सकते हैं कि "आप एक ही कक्षा का एक नया उदाहरण बनाकर और मूल्यों पर प्रतिलिपि बनाकर कॉपी ऑपरेशन को कार्यान्वित कर सकते हैं"? क्या मेरे पास एसक्यूएलए मैप किए गए वर्ग के लिए '__deepcopy __()' के "स्थानीयकृत" संस्करण को परिभाषित किया गया है? मुझे लगता है कि आखिरी पंक्ति से मेरा मतलब क्या है, लेकिन क्या आप कृपया इसे थोड़ा अधिक ठोस रूप से स्पष्ट कर सकते हैं? शायद एक उदाहरण? – PizzAzzra

+0

मेरा मतलब था कि 'def __deepcopy __ (self, memo): वापसी छात्र (deepcopy (self.sid, memo), deepcopy (self.name, memo), deepcopy (self.allocated_project, memo), deepcopy (self। allocated_rank, ज्ञापन)) '(ध्यान दें कि यदि आप ऐसा करते हैं तो आप प्राथमिक कुंजी के रूप में 'sid' का उपयोग नहीं कर पाएंगे)। –

+0

@ डेविड: प्रश्न अपडेट किया है। '__deepcopy __()' विधि मेरे लिए थोड़ी अधिक है क्योंकि मैं सामान लपेट रहा हूं, हालांकि, मैंने आपके "अलग-अलग वर्ग-समान फ़ील्ड-अप्रयुक्त" समाधान का पालन किया है। क्या आप इस मामले में सबक्लास द्वारा अपना मतलब बता सकते हैं? – PizzAzzra

2

यहाँ एक और विकल्प है, लेकिन मुझे यकीन है कि यह आपकी समस्या के लिए लागू है नहीं कर रहा हूँ:

  1. सभी आवश्यक संबंधों के साथ-साथ डेटाबेस से वस्तुओं को पुनः प्राप्त। आप या तो lazy='joined' या lazy='subquery' संबंधों के लिए पास कर सकते हैं, या options(eagerload(relation_property) क्वेरी की विधि पर कॉल कर सकते हैं, या बस अपने लोड को ट्रिगर करने के लिए आवश्यक गुणों तक पहुंच सकते हैं।
  2. सत्र से ऑब्जेक्ट का विस्तार करें। ऑब्जेक्ट गुणों की आलसी लोडिंग इस बिंदु से समर्थित नहीं होगी।
  3. अब आप वस्तु को सुरक्षित रूप से संशोधित कर सकते हैं।
  4. जब आपको डेटाबेस में ऑब्जेक्ट को अपडेट करने की आवश्यकता होती है तो आपको उसे सत्र में वापस ले जाना होगा और प्रतिबद्ध होना होगा।

अद्यतन:

from sqlalchemy import * 
from sqlalchemy.ext.declarative import declarative_base 
from sqlalchemy.orm import sessionmaker, relation, eagerload 

metadata = MetaData() 
Base = declarative_base(metadata=metadata, name='Base') 

class Project(Base): 
    __tablename__ = 'projects' 
    id = Column(Integer, primary_key=True) 
    name = Column(String) 


class Student(Base): 
    __tablename__ = 'students' 
    id = Column(Integer, primary_key=True) 
    project_id = Column(ForeignKey(Project.id)) 
    project = relation(Project, 
         cascade='save-update, expunge, merge', 
         lazy='joined') 

engine = create_engine('sqlite://', echo=True) 
metadata.create_all(engine) 
session = sessionmaker(bind=engine)() 

proj = Project(name='a') 
stud = Student(project=proj) 
session.add(stud) 
session.commit() 
session.expunge_all() 
assert session.query(Project.name).all()==[('a',)] 

stud = session.query(Student).first() 
# Use options() method if you didn't specify lazy for relations: 
#stud = session.query(Student).options(eagerload(Student.project)).first() 
session.expunge(stud) 

assert stud not in session 
assert stud.project not in session 

stud.project.name = 'b' 
session.commit() # Stores nothing 
assert session.query(Project.name).all()==[('a',)] 

stud = session.merge(stud) 
session.commit() 
assert session.query(Project.name).all()==[('b',)] 
+0

मुझे एसक्यूएलए के साथ बहुत अनुभवी नहीं है ... क्या मुझे इस विधि का एक और विशिष्ट उदाहरण देना संभव होगा? – PizzAzzra

+0

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