在失败的事务中执行一些SQL代码

时间:2016-09-15 17:18:28

标签: sqlite transactions cherrypy peewee

在我的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会引发异常,以便重定向到显示刚刚记录的消息的页面

由于在事务中异常上升,Table1do_this的更新和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))

1 个答案:

答案 0 :(得分:0)

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

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(), '/')