这是我第一次使用ORM,所以我不确定处理这个问题的最佳方法。我有一对多的关系,每个家长可以有很多孩子:
class Parent(Base):
__tablename__ = 'Parent'
name = Column(String(50))
gid = Column(String(16), primary_key = True)
lastUpdate = Column(DateTime)
def __init__(self,name, gid):
self.name = name
self.gid = gid
self.lastUpdate = datetime.datetime.now()
class Child(Base):
__tablename__ = 'Child'
id = Column(Integer, primary_key = True)
loc = Column(String(50))
status = Column(String(50))
parent_gid = Column(String(16), ForeignKey('Parent.gid'))
parent = relationship("Parent", backref=backref('children'))
现在,通过网络进行更新。当有更新时,我想更新相应的Parent行(更新lastUpdate列)并将新子行插入数据库。我不知道如何用ORM做到这一点。这是我失败的尝试:
engine = create_engine('sqlite+pysqlite:///file.db',
module=dbapi2)
Base.metadata.create_all(engine)
session = sessionmaker(bind=engine)()
def addChildren(parent):
p = session.query(Parent).filter(Parent.gid == p1.gid).all()
if len(p) == 0:
session.add(p1)
session.commit()
else:
updateChildren = parent.children[:]
parent.chlidren = []
for c in updateChildren:
c.parent_gid = parent.gid
session.add_all(updateChildren)
session.commit()
if __name__ == '__main__':
#first update from the 'network'
p1 = Parent(name='team1', gid='t1')
p1.children = [Child(loc='x', status='a'), Child(loc='y', status='b')]
addChildren(p1)
import time
time.sleep(1)
#here comes another network update
p1 = Parent(name='team1', gid='t1')
p1.children = [Child(loc='z', status='a'), Child(loc='k', status='b')]
#this fails
addChildren(p1)
我最初尝试进行合并,但这导致旧的子节点与父节点解除关联(外部ID设置为null)。使用ORM解决此问题的最佳方法是什么?感谢
修改
我想在通过网络进行更新时创建全新的对象并没有多大意义。我应该只查询相应父节点的会话,然后在必要时创建新子节点并合并? E.g。
def addChildren(pname, pid, cloc, cstat):
p = session.query(Parent).filter(Parent.gid == pid).all()
if len(p) == 0:
p = Parent(pname, pid)
p.children = [Child(loc=cloc, status=cstat)]
session.add(p)
session.commit()
else:
p = p[0]
p.children.append(Child(loc=cloc, status=cstat))
session.merge(p)
session.commit()
答案 0 :(得分:30)
你是对的 - 你不应该两次创建同一个父母。在添加孩子方面,......好吧,你真的只需要添加它们而你不关心现有的......所以你编辑的代码应该可以正常工作。您可以使它更短,更易读:
def addChildren(pname, pid, cloc, cstat):
p = session.query(Parent).get(pid) # will give you either Parent or None
if not(p):
p = Parent(pname, pid)
session.add(p)
p.children.append(Child(loc=cloc, status=cstat))
session.commit()
这种方式的缺点是,对于现有的Parent,在添加新Child并稍后将其保存到数据库之前,将整个Children集合加载到内存中。如果是这种情况(每个父母的子女数量越来越多),那么lazy='noload'
可能会有用:
parent = relationship("Parent", backref=backref('children', lazy='noload'))
这可能会显着提高插入速度,但在这种情况下,对p.children
的访问将从不从数据库加载现有对象。在这种情况下,定义另一种关系就足够了。在这些情况下,我更喜欢使用Building Query-Enabled Properties,因此最终只有一个属性 用于添加对象,而另一个仅用于查询持久性结果,这通常是由系统的不同部分使用。