如何告诉SQLAlchemy为Session.merge(而不是PK)使用不同的身份规则?

时间:2016-09-21 12:32:58

标签: python sqlalchemy

我有一个遗留数据库,它是用自动增量ID盲目创建的,即使表中有一个完全有效的自然键。

最终会出现代码乱码的代码:

Fetch row with natural key 'x'
if exists:
    update row with NK 'x'
else:
    insert row with NK 'x'

基本上是一个upsert。

这个用例(upsert)由SQLAlchemy的Session.merge()涵盖。但SA只会查看表的主键以协调它是否必须进行插入或更新。然而,在现有的数据库中,PK确实 - 与应该所做的相反 - 不代表行的真实身份。因此,相同的标识可以与多个自动增量ID一起出现。还有一些其他业务规则可以确保唯一性。但是今天的ID 1明天可以是ID 3246

目前还没有很好的方法以合理的方式修改数据库,因为太多的遗留应用程序依赖于结构。

为了一个有形的例子,假设我们在表中有网络设备,并将其主机名作为自然键。 当前数据库看起来像这样:

CREATE TABLE device (
    id SERIAL PRIMARY KEY,
    hostname TEXT UNIQUE,
    some_other_column TEXT
)

相应的SA模型:

class Device(Base):
    id = Column(Integer, primary_key=True)
    hostname = Column(String(256))
    some_other_column = Column(String(20))

我希望能够做到以下几点:

mydevice = Device(hostname='hello-world', some_other_column='foo')
merged_device = session.merge(mydevice)
session.commit()

在这个例子中,我希望SA进行“插入或更新”。但是使用当前模型,这实际上会导致错误(由于唯一的主机名约束)。

可以hostname列指定为SA模型中的主键(并将PK保留在DB中)。但这看起来有点黑客。是否有更明确和可理解的方式告诉SQLAlchemy它应该使用“hostname”作为身份?如果是的话,怎么样?

1 个答案:

答案 0 :(得分:1)

在这样的情况下,我发现最好谎言到sqlalchemy。告诉它自然键是主键。

class Device(Base):
    hostname = Column(String(256), primary_key=True)
    some_other_column = Column(String(20))