<p>您可以对事务执行<code>rollback</code>,也可以在引发异常的代码之前回滚到保存点(cr是游标):</p>
<pre><code>name = uuid.uuid1().hex
cr.execute('SAVEPOINT "%s"' % name)
try:
# your failing query goes here
except Exception:
cr.execute('ROLLBACK TO SAVEPOINT "%s"' % name)
# your alternative code goes here
else:
cr.execute('RELEASE SAVEPOINT "%s"' % name)
</code></pre>
<p>此代码假定有正在运行的事务,否则您将不会收到该错误消息。</p>
<p>Django postgresql后端<a href="https://github.com/django/django/blob/cecc079168e8669138728d31611ff3a1e7eb3a9f/django/db/backends/postgresql/base.py#L213" rel="nofollow noreferrer">creates cursors</a>直接来自<a href="http://initd.org/psycopg/docs/cursor.html" rel="nofollow noreferrer">psycopg</a>。也许将来他们会为Django游标创建一个代理类,类似于<a href="https://github.com/odoo/odoo/blob/fc2e80cb4bcc450762c7ac5cb82a3e2d88062b38/odoo/sql_db.py#L61" rel="nofollow noreferrer">cursor of odoo</a>。它们用<a href="https://github.com/odoo/odoo/blob/fc2e80cb4bcc450762c7ac5cb82a3e2d88062b38/odoo/sql_db.py#L396" rel="nofollow noreferrer">following code</a>扩展游标(self是游标):</p>
<pre><code>@contextmanager
@check
def savepoint(self):
"""context manager entering in a new savepoint"""
name = uuid.uuid1().hex
self.execute('SAVEPOINT "%s"' % name)
try:
yield
except Exception:
self.execute('ROLLBACK TO SAVEPOINT "%s"' % name)
raise
else:
self.execute('RELEASE SAVEPOINT "%s"' % name)
</code></pre>
<p>这样,上下文可以使代码更简单,它将是:</p>
<pre><code>try:
with cr.savepoint():
# your failing query goes here
except Exception:
# your alternative code goes here
</code></pre>
<p>而且代码可读性更强,因为事务处理的东西不在那里。</p>