Piz*_*zra 9 python sqlalchemy copy
我想我在上一个问题中试图提出太多要求,为此道歉.让我以这种方式尽可能简单地阐述我的情况.
基本上,我有一堆字典引用我的对象,然后使用SQLAlchemy进行映射.一切都很好.但是,我想对这些词典的内容进行迭代更改.问题是,这样做会改变它们引用的对象---并且使用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)
Run Code Online (Sandbox Code Playgroud)
因此,我将要更改的属性是allocated_proj_ref和allocated_rank属性.将students_table使用唯一的学生证键控(sid).
Question
我想要坚持上面改变的属性 - 我的意思是,这基本上就是我决定使用SQLA的原因.但是,映射的对象将会更改,不建议这样做.因此,如果我对doppelgänger,未映射的对象进行更改...我可以进行这些更改并更新映射对象的字段/表.
从某种意义上说,我正在遵循David的二次解决方案,在那里我创建了另一个未映射的Class版本.
我尝试使用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
Run Code Online (Sandbox Code Playgroud)
这是否意味着Student 必须映射?
Health warning!
有人在这里指出了一个非常好的附加问题.看,即使我正在调用copy.deepcopy()非映射对象,在这种情况下,让我们假设它是我上面定义的学生字典,deepcopy会复制一切.我allocated_proj_ref实际上是一个Project对象,我有一个相应的projects字典.
所以我deepcopy的都students和projects-这我-他说我得情况下students的allocated_proj_ref属性会发生问题与实例相匹配projects的字典.
因此,我认为我将不得不重新定义/覆盖(这就是所谓的不是它?)deepcopy在每个类中使用def __deecopy__(self, memo):或类似的东西?
我想覆盖__deepcopy__它忽略所有SQLA的东西(哪些是<class 'sqlalchemy.util.symbol'>和<class 'sqlalchemy.orm.state.InstanceState'>),但复制其他所有映射类的一部分.
有什么建议吗?
如果我没有记错/思考的话,在 SQLAlchemy 中,通常一次只有一个对象对应于给定的数据库记录。这样做是为了让 SQLAlchemy 可以保持 Python 对象与数据库同步,反之亦然(好吧,如果存在来自 Python 外部的并发数据库突变,则不会,但那是另一个故事了)。所以问题是,如果您要复制这些映射对象之一,您最终会得到两个对应于同一数据库记录的不同对象。如果您更改其中一个,那么它们将具有不同的值,并且数据库无法同时匹配它们。
我认为您可能需要做的是决定是否希望数据库记录反映您在更改副本属性时所做的更改。如果是这样,那么您根本不应该复制对象,而应该只是重用相同的实例。
另一方面,如果您不希望在更新副本时更改原始数据库记录,您还有另一种选择:副本是否应该成为数据库中的新行?或者它根本不应该映射到数据库记录?在前一种情况下,您可以通过创建同一类的新实例并复制值来实现复制操作,这与创建原始对象的方式几乎相同。这可能会在__deepcopy__()SQLAlchemy 映射类的方法中完成。在后一种情况(无映射)中,您需要一个单独的类,该类具有所有相同的字段,但不使用 SQLAlchemy 进行映射。实际上,让 SQLAlchemy 映射类成为该非映射类的子类,并且只为子类进行映射可能更有意义。
编辑:好的,澄清我最后一点的意思:现在你有一个Student用来代表你的学生的班级。我建议你创建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
Run Code Online (Sandbox Code Playgroud)
并有一个子类,例如StudentDBRecord,它将映射到数据库。
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)})
Run Code Online (Sandbox Code Playgroud)
现在,您将使用Student未映射的 实例来实现优化算法 - 因此,当Student对象的属性发生变化时,数据库不会发生任何变化。这意味着您可以安全地使用copy或deepcopy根据需要。完成后,您可以将Student实例更改为StudentDBRecord实例,例如
students = ...dict with best solution...
student_records = [StudentDBRecord(s) for s in students.itervalues()]
session.commit()
Run Code Online (Sandbox Code Playgroud)
这将创建与处于最佳状态的所有学生相对应的映射对象,并将它们提交到数据库。
编辑2:所以也许这不起作用。一个快速修复方法是将Student构造函数复制到StudentDBRecord并进行StudentDBRecord扩展object。也就是说,将之前的定义替换StudentDBRecord为:
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
Run Code Online (Sandbox Code Playgroud)
或者如果你想概括它:
class StudentDBRecord(object):
def __init__(self, student):
for attr in dir(student):
if not attr.startswith('__'):
setattr(self, attr, getattr(student, attr))
Run Code Online (Sandbox Code Playgroud)
后一个定义会将 的所有非特殊属性复制Student到StudentDBRecord.