在Odoo ORM中处理SQL约束异常

Lud*_*mer 8 python orm exception openerp odoo

我的模型中定义了一个UNIQUE约束:

class FooBar(models.Model):
    _name = 'my.foobar'

    # ...

    _sql_constraints = [
        ('foo_bar_uniq', 'unique("foo", "bar")', 'You could not step twice into the same foobar!')
    ]
Run Code Online (Sandbox Code Playgroud)

以及一个用于创建新对象的代码的控制器:

class FooBarController(http.Controller):
    @http.route('/foobar/create/', auth='public', website=True)
    def create(self, foo, bar):
        http.request.env['my.foobar'].create({
            'foo': foo,
            'bar': bar,
        })
        return http.request.render('my.thank_you_page')
Run Code Online (Sandbox Code Playgroud)

如果违反了UNIQUE约束,我会得到一个IntegrityError例外.我想抓住它并向用户显示不同的消息:

from psycopg2 import IntegrityError

class FooBarController(http.Controller):
    @http.route('/foobar/create/', auth='public', website=True)
    def create(self, foo, bar):
        try:
            http.request.env['my.foobar'].create({
                'foo': foo,
                'bar': bar,
            })
            return http.request.render('my.thank_you_page')
        except IntegrityError:
            return http.request.render('my.error_page')
Run Code Online (Sandbox Code Playgroud)

这有点......有点儿.在IntegrityError成功地抓住了,但随后的所有数据库操作(其中,因为据我所知,通过自动的触发website模块)结束InternalError:

InternalError: current transaction is aborted, commands ignored until end of transaction block.
Run Code Online (Sandbox Code Playgroud)

因此,最终用户看到的只是Internal Server Error页面.

如何正确处理UNIQUE约束违规?

小智 5

在catch代码中要做的第一件事是提交或关闭数据库游标以释放锁.


Lud*_*mer 5

这是一个扩展伊曼纽尔答案的例子:

class FooBarController(http.Controller):
    @http.route('/foobar/create/', auth='public', website=True)
    def create(self, foo, bar):
        try:
            http.request.env['my.foobar'].create({
                'foo': foo,
                'bar': bar,
            })
            return http.request.render('my.thank_you_page')
        except IntegrityError:
            # can't use the usual `http.request.env.cr` style,
            # because `env` queries db and everything explodes
            http.request._cr.rollback()
            return http.request.render('my.error_page')
Run Code Online (Sandbox Code Playgroud)


Nag*_*lis 3

您可以使用cr.savepoint() 上下文管理器:

class FooBarController(http.Controller):

    @http.route('/foobar/create/', auth='public', website=True)
    def create(self, foo, bar):
        try:
            with http.request.env.cr.savepoint():
                http.request.env['my.foobar'].create({
                    'foo': foo,
                    'bar': bar,
                })
                return http.request.render('my.thank_you_page')
        except IntegrityError:
            return http.request.render('my.error_page')
Run Code Online (Sandbox Code Playgroud)

上下文管理器内的任何数据库调用都将在PostgreSQL 保存点内运行。如果发生异常,保存点(而不是事务)将被回滚,因此您将能够在当前数据库事务内进行后续调用。

此外,如果您不希望IntegrityError记录 s,您可以使用上下文管理器/装饰器暂时静音openerp.sql_db记录器(或者odoo.sql_db,如果您使用的是 Odoo 10 或更高版本) :mute_logger

from openerp.tools import mute_logger

# ...

try:
    with mute_logger('openerp.sql_db'), http.request.env.cr.savepoint():
        # ...
except IntegrityError:
    # ...
Run Code Online (Sandbox Code Playgroud)