【问题标题】:Executing some SQL code in a failed transaction在失败的事务中执行一些 SQL 代码
【发布时间】:2017-01-23 19:40:04
【问题描述】:

在我的 CherryPy + Peewee 应用程序中,我经常使用以下模式:启动事务、执行操作列表并返回显示结果的页面。如果在任何操作过程中出现问题,我会在日志表中添加一行,然后重定向到显示该行的页面。

问题是 CherryPy 中的重定向是通过引发异常来执行的,异常会导致一些事务回滚。回滚是失败的操作(以及所有先前的操作,即使它们成功)所需要的,但这不是我想要的日志记录。

例如,如果用户转到a_page?a=1&b=2&c=3,则使用以下代码:

  • do_this 会找到 x != y 而不会执行 show_message
  • do_this 将更新Table1 上的一条记录
  • do_that 会找到 x == y 并执行 show_message
  • show_message 将在Message 日志表中添加一行
  • show_message 将引发异常以重定向到将显示刚刚记录的消息的页面

由于在事务中引发了异常,因此在do_this 中对Table1 进行的更新和在show_message 中记录的消息都将被回滚。

如何提交记录表中的行并回滚所有其他更改?

@cherrypy.expose
def a_page(self, a, b, c):
    with db.transaction():
        self.do_this(a, b)
        self.do_that(b, c)
    return render('it_worked.html')

def do_this(self, x, y):
    if x == y:
        self.show_message('Wrong this')
    Table1.update(f2=x).where(f1 == y).execute()

def do_that(self, x, y):
    if x != y:
        self.show_message('Wrong that')
    Table1.update(f3=x).where(f1 == z).execute()

def show_message(self, message)
    msg = Message.create(msg=message)
    raise cherrypy.HTTPRedirect('show_message?id={}'.format(msg.id))

【问题讨论】:

  • 可能是嵌套事务,即保存点。
  • @CL。据我所知,嵌套事务不允许您保存内部操作。我需要相反:我不想保存直到现在执行的事务,但我想保存最后一个。
  • 我找到了一个我不喜欢的解决方案,因为 (1) 它需要将事务传递给函数,并且 (2) 如果存在嵌套事务,它将无法工作。在我下面的回答中。请让我知道这是否是一个好的解决方案,或者我可以做得更好
  • 您必须在回滚到保存点后写入日志。
  • @CL。这就是我在下面的示例中所做的。 Log.show_log() 需要知道最外层的事务才能回滚,我不知道我是否喜欢。

标签: sqlite transactions cherrypy peewee


【解决方案1】:

这个解决方案似乎可行,但我不喜欢,因为它需要将事务向下传递给函数,而且它可能(还不确定)不适用于嵌套事务。

import cherrypy
import peewee

db = peewee.SqliteDatabase('test.db', threadlocals=True)

class PeeweeModel(peewee.Model):
    class Meta:
        database = db

class Table1(PeeweeModel):
    field1 = peewee.CharField()

Table1.drop_table(True)
Table1.create_table(True)

class Log(PeeweeModel):
    msg = peewee.CharField()

    @staticmethod
    def show_log(msg, txn):
        txn.rollback()
        msg = Log.create(msg=msg)
        txn.commit()
        raise cherrypy.HTTPRedirect('show_log?msg_id={}'.format(msg.id))

Log.drop_table(True)
Log.create_table(True)

def table_content():
    return '<br>'.join(['{} {}'.format(row.id, row.field1) for row in Table1.select()])

html = """<!DOCTYPE HTML>
<html>
<head>
</head>
<body>
<a href="index">index</a><br>
<a href="add_row">add_row</a><br>
{}<br>
Table1<br>
{}<br>
</body>
</html>"""

class App():

    @cherrypy.expose
    def index(self, msg='Hello'):
        return html.format(msg, table_content())

    @cherrypy.expose
    def add_row(self):
        with db.transaction() as txn:
            Table1.update(field1=Table1.field1 + 1).execute()
            Table1.update(field1=Table1.field1 + 1).where(Table1.id == 2).execute()
            if Table1.select().count() == 5:
                raise Log.show_log('Something went wrong', txn)
            Table1.create(field1=1, field2=2)
        return html.format('Added record to Table1', table_content())

    @cherrypy.expose
    def show_log(self, msg_id):
        msg = Log.get(Log.id == msg_id)
        text = 'Message id {}: {}'.format(msg.id, msg.msg)
        return html.format(text, table_content())

cherrypy.quickstart(App(), '/')

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多