PonyORM(Python)"价值在当前交易之外更新"但它不是

时间:2016-11-29 16:47:40

标签: python sql orm transactions ponyorm

我在磁盘上使用Pony ORM 0.7版和Sqlite3数据库,并遇到了这个问题:我正在执行select,然后是更新,然后是select,然后是另一个更新,并收到错误消息

pony.orm.core.UnrepeatableReadError: Value of Task.order_id for
   Task[23654] was updated outside of current transaction (was: 1, now: 2)

我已将问题减少到导致问题的最小命令集(即删除任何内容导致问题不会发生):

@db_session
def test_method():
    tasks = list(map(Task.to_dict, Task.select()))
    db.execute("UPDATE Task SET order_id=order_id*2")
    task_to_move = select(task for task in Task if task.order_id == 2).first()
    task_to_move.order_id = 1

test_method()

为了完整起见,以下是Task

的定义
class Task(db.Entity):
    text = Required(unicode)
    heading = Required(int)
    create_timestamp = Required(datetime)
    done_timestamp = Optional(datetime)
    order_id = Required(int)

另外,如果我从select中删除task.order_id == 2的约束,则问题不再出现,所以我认为问题与基于自事务开始以来已更改的字段的查询有关,但我不知道为什么错误消息告诉我它被另一个事务更改(除非db.execute可能在单独的事务中执行,因为它是原始SQL?)

我已经查看了这个类似的问题,但问题不同(Pony ORM reports record "was updated outside of current transaction" while there is not other transaction)和此文档(https://docs.ponyorm.com/transactions.html),但都没有解决我的问题。

任何想法可能会在这里发生什么?

1 个答案:

答案 0 :(得分:2)

Pony默认使用乐观并发控制。对于每个属性,Pony会记住它的当前值(可能由应用程序代码修改)以及从数据库中读取的原始值。在UPDATE期间,Pony会检查数据库中列的值是否仍然相同。如果值被更改,Pony会假定某些并发事务执行此操作,并抛出异常以避免“丢失更新”情况。

如果执行一些原始SQL查询,Pony不知道数据库中究竟修改了什么。因此,当Pony遇到计数器值被更改时,它错误地认为该值已被另一个事务更改。

为了避免此问题,您可以将order_id属性标记为volatile。然后Pony会假设属性的值可以随时改变(通过触发器或原始SQL更新),并将从乐观检查中排除该属性:

class Task(db.Entity):
    text = Required(unicode)
    heading = Required(int)
    create_timestamp = Required(datetime)
    done_timestamp = Optional(datetime)
    order_id = Required(int, volatile=True)

请注意,Pony将缓存volatile属性的值,并且在保存对象之前不会重新读取数据库中的值,因此在某些情况下,您可以在Python中获得过时的值。

<强>更新

从版本0.7.4开始,您还可以为optimistic=False指定db_session选项,以关闭对使用原始SQL查询的特定事务的乐观检查:

with db_session(optimistic=False):
    ...

@db_session(optimistic=False)
def some_function():
    ...

此外,现在可以为属性指定optimistic=False选项,而不是指定volatile=True。然后Pony不会对该属性进行乐观检查,但仍会考虑将其视为非易失性