【发布时间】:2011-11-02 05:16:38
【问题描述】:
我有一条记录,如果它不存在,我希望它存在于数据库中,如果它已经存在(主键存在),我希望将字段更新到当前状态。这通常称为upsert。
以下不完整的代码 sn-p 演示了什么会起作用,但它似乎过于笨拙(特别是如果有更多的列)。什么是更好/最好的方法?
Base = declarative_base()
class Template(Base):
__tablename__ = 'templates'
id = Column(Integer, primary_key = True)
name = Column(String(80), unique = True, index = True)
template = Column(String(80), unique = True)
description = Column(String(200))
def __init__(self, Name, Template, Desc):
self.name = Name
self.template = Template
self.description = Desc
def UpsertDefaultTemplate():
sess = Session()
desired_default = Template("default", "AABBCC", "This is the default template")
try:
q = sess.query(Template).filter_by(name = desiredDefault.name)
existing_default = q.one()
except sqlalchemy.orm.exc.NoResultFound:
#default does not exist yet, so add it...
sess.add(desired_default)
else:
#default already exists. Make sure the values are what we want...
assert isinstance(existing_default, Template)
existing_default.name = desired_default.name
existing_default.template = desired_default.template
existing_default.description = desired_default.description
sess.flush()
有没有更好或更简洁的方法来做到这一点?像这样的东西会很棒:
sess.upsert_this(desired_default, unique_key = "name")
虽然unique_key kwarg 显然是不必要的(ORM 应该能够轻松解决这个问题),但我添加它只是因为 SQLAlchemy 倾向于只使用主键。例如:我一直在研究Session.merge 是否适用,但这仅适用于主键,在这种情况下,主键是一个自动递增的 id,对此目的并不是非常有用。
一个示例用例是在启动可能已升级其默认预期数据的服务器应用程序时。即:此 upsert 没有并发问题。
【问题讨论】:
-
如果
name字段是唯一的,为什么不能将其设为主键(在这种情况下合并会起作用)。为什么需要单独的主键? -
@abbot:我不想进入 id 字段辩论,但是......简短的回答是“外键”。更长的是,虽然名称确实是唯一需要的唯一键,但存在两个问题。 1) 当模板记录被另一个表中的 5000 万条记录引用时,将该 FK 作为字符串字段是疯狂的。索引整数更好,因此看似毫无意义的 id 列。并且 2) 在此基础上进行扩展,如果字符串 用作 FK,那么现在有两个位置可以在名称更改时更新名称,这很烦人并且充斥着死关系问题。 id 从不改变。
-
你可以尝试一个新的(测试版)upsert library for python...它与 psycopg2、sqlite3、MySQLdb 兼容
标签: python sqlalchemy upsert