2014-11-17 2 views
8

Я получил УНИКАЛЬНОЕ ограничение, определенное в моей модели:Обработка исключений SQL ограничений в Odoo ОРМЕ

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

    # ... 

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

и контроллере с кодом для создания новых объектов:

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') 

Если 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') 

Это работает ... любопытно. IntegrityError успешно поймана, но все последующие операции базы данных (которые, насколько я знаю, являются триггер автоматически website модуля) конца в InternalError:

InternalError: current transaction is aborted, commands ignored until end of transaction block. 

Как следствие, все, что видит конечный пользователь страница Internal Server Error.

Как я могу правильно обрабатывать УНИКАЛЬНЫЕ нарушения ограничений?

ответ

1

Вы можете использовать менеджер 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') 

Любая база данных вызовов внутри менеджера контекста будет работать внутри PostgreSQL savepoint. Если возникает исключение, точка сохранения (а не транзакция) будет отклонена, поэтому вы сможете совершать последующие вызовы внутри текущей транзакции базы данных.

Кроме того, если вы не хотите 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: 
    # ... 
+0

Это отличный ответ, спасибо! Мне понадобилось пару лет для меня слишком поздно, но я с удовольствием приму это. –

5

Первое, что нужно сделать в вашем коде catch, заключается в фиксации или закрытии курсора базы данных для освобождения блокировки.

+0

Благодарим за отзыв. [Этот FAQ] (http://initd.org/psycopg/docs/faq.html#problems-with-transactions-handling) в документации 'psycopg' говорит о том, что откат может быть лучше? –

4

Вот пример расширения на ответ Эмануэль:

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') 
Смежные вопросы