sqlalchemy完整性错误

时间:2013-08-03 08:22:15

标签: python sqlalchemy

我有一张表,其中主键(id)不是我区分记录的键。为此,我对3列有一个独特的约束。 为了能够合并记录,我添加了一个类方法来检索相关记录(如果存在),否则返回一个新记录。

class Foo(Base):
    __table_args__ = (sa.UniqueConstraint('bar', 'baz', 'qux'),)

    id = sa.Column(Identifier, sa.Sequence('%s_id_seq' % __tablename__), nullable=False, primary_key=True)
    bar = sa.Column(sa.BigInteger)
    baz = sa.Column(sa.BigInteger)
    qux = sa.Column(sa.BigInteger)
    a1 = sa.Column(sa.BigInteger)
    a2 = sa.Column(sa.BigInteger)

    @classmethod
    def get(cls, bar=None, baz=None, qux=None, **kwargs):
        item = session.query(cls).\
            filter(cls.bar== bar).\
            filter(cls.baz == baz).\
            filter(cls.qux == qux).\
            first()

        if item:
            for k, v in kwargs.iteritems():
                if getattr(item, k) != v:
                    setattr(item, k, v)
        else:
            item = cls(bar=bar, baz=baz, qux=qux, **kwargs)

        return item

这在大多数情况下运行良好,但每隔一段时间,我在尝试合并项目时会收到​​完整性错误:

foo = Foo.get(**item)
session.merge(foo)

据我了解,这是因为合并尝试插入记录,其中具有唯一字段的记录已经存在。

get功能有问题吗?我在这里缺少什么?

(顺便说一句:我意识到这可能看起来很尴尬,但我需要一个唯一的顺序ID,以避免DB不支持非主键列上的序列的问题,我这样做了)

编辑1: 将orm.db更改为session,以便示例更清晰

编辑2: 我有这个系统在几个平台上运行,似乎这只发生在Ubuntu之上的mysql(其他平台是RedHat之上的Oracle)。此外,以某种奇怪的方式,它对特定的最终用户发生的更多。 关于mysql,我尝试了mysql和mysql + mysqldb作为连接字符串,但都产生了这个错误。 关于最终用户,没有任何意义,我也不知道该怎么做... 关于mysql,

2 个答案:

答案 0 :(得分:2)

确实,您的方法容易出现完整性错误。当你调用Foo.get(1,2,3)2次时,你不会在两者之间刷新会话。第二次调用它时,ORM查询再次失败 - 因为数据库中还没有实际的行 - 并且创建了一个具有不同标识的新对象。然后在提交/刷新这两个冲突导致完整性错误。这可以通过在每次创建对象后刷新DB来避免。

现在,如果在merge / ORM get中已知主键,则SQLAlchemy的工作方式不同 - 如果找到匹配的主键已经在会话中加载,则SQLAlchemy意识到这两个必须是同一个对象。但是,没有对唯一索引进行此类检查。也有可能这样做。然而,它会使比赛条件变得更加罕见,因为可能会有2个会话同时创建相同的(bar,baz,qux)三元组。

TL; DR:

else:
    item = cls(bar=bar, baz=baz, qux=qux, **kwargs)
    session.add(item)
    session.flush()

答案 1 :(得分:0)

我似乎找到了罪魁祸首!

昨天我遇到了客户因错误的时区而导致IntegrityError的情况,所以它让我思考这个方向。 我用来识别模型的一个字段是Date列(我不知道它是相关的,因此我甚至没有提到它,抱歉......),但是,因为我从Flex客户端调用了这些操作使用AMF,并且由于actionscript没有Time对象没有时间,我正在使用零时间传输Date对象。我想在某些情况下,我在那些提出IntegrityError的日期有不同的时间。

我原本希望SA在Date列的情况下从日期值中剥离时间,因为我认为DB会有,所以Model.date_column == datetime_value应该在进行比较之前将datetime强制转换为datetime.date。

至于我的解决方案,我只是确保在查询数据库之前将值强制转换为datetime.date()... 到目前为止,昨天和今天很安静,没有任何抱怨。如果发生任何变化,我会继续留意并报告......

谢谢大家的帮助。 干杯, 奥菲尔