# HG changeset patch # User Alexandre Fayolle # Date 1284136238 -7200 # Node ID e9d125fd14656decaa3c152e82c5bc2c751cdaab # Parent d36104de8459d40bd8bf02b0b92fc0d64de1b742 nicer error reporting for unique together constraints diff -r d36104de8459 -r e9d125fd1465 _exceptions.py --- a/_exceptions.py Fri Sep 10 17:58:38 2010 +0200 +++ b/_exceptions.py Fri Sep 10 18:30:38 2010 +0200 @@ -80,6 +80,8 @@ class MultiSourcesError(RepositoryError, InternalError): """usually due to bad multisources configuration or rql query""" +class UniqueTogetherError(RepositoryError): + """raised when a unique_together constraint caused an IntegrityError""" # security exceptions ######################################################### diff -r d36104de8459 -r e9d125fd1465 server/repository.py --- a/server/repository.py Fri Sep 10 17:58:38 2010 +0200 +++ b/server/repository.py Fri Sep 10 18:30:38 2010 +0200 @@ -50,12 +50,12 @@ UnknownEid, AuthenticationError, ExecutionError, ETypeNotSupportedBySources, MultiSourcesError, BadConnectionId, Unauthorized, ValidationError, - RepositoryError, typed_eid, onevent) + RepositoryError, UniqueTogetherError, typed_eid, onevent) from cubicweb import cwvreg, schema, server from cubicweb.server import utils, hook, pool, querier, sources from cubicweb.server.session import Session, InternalSession, InternalManager, \ security_enabled - +_ = unicode def del_existing_rel_if_needed(session, eidfrom, rtype, eidto): """delete existing relation when adding a new one if card is 1 or ? @@ -1062,7 +1062,14 @@ entity._cw_set_defaults() if session.is_hook_category_activated('integrity'): entity._cw_check(creation=True) - source.add_entity(session, entity) + try: + source.add_entity(session, entity) + except UniqueTogetherError, exc: + etype, rtypes = exc.args + problems = {} + for col in rtypes: + problems[col] = _('violates unique_together constraints (%s)') % (','.join(rtypes)) + raise ValidationError(entity.eid, problems) self.add_info(session, entity, source, extid, complete=False) entity._cw_is_saved = True # entity has an eid and is saved # prefill entity relation caches @@ -1133,14 +1140,22 @@ relations.append((attr, entity[attr], previous_value)) if source.should_call_hooks: # call hooks for inlined relations - for attr, value, _ in relations: + for attr, value, _t in relations: hm.call_hooks('before_add_relation', session, eidfrom=entity.eid, rtype=attr, eidto=value) if not only_inline_rels: hm.call_hooks('before_update_entity', session, entity=entity) if session.is_hook_category_activated('integrity'): entity._cw_check() - source.update_entity(session, entity) + try: + source.update_entity(session, entity) + except UniqueTogetherError, exc: + etype, rtypes = exc.args + problems = {} + for col in rtypes: + problems[col] = _('violates unique_together constraints (%s)') % (','.join(rtypes)) + raise ValidationError(entity.eid, problems) + self.system_source.update_info(session, entity, need_fti_update) if source.should_call_hooks: if not only_inline_rels: diff -r d36104de8459 -r e9d125fd1465 server/sources/native.py --- a/server/sources/native.py Fri Sep 10 17:58:38 2010 +0200 +++ b/server/sources/native.py Fri Sep 10 18:30:38 2010 +0200 @@ -34,6 +34,7 @@ from base64 import b64decode, b64encode from contextlib import contextmanager from os.path import abspath +import re from logilab.common.compat import any from logilab.common.cache import Cache @@ -44,7 +45,7 @@ from yams import schema2sql as y2sql -from cubicweb import UnknownEid, AuthenticationError, ValidationError, Binary +from cubicweb import UnknownEid, AuthenticationError, ValidationError, Binary, UniqueTogetherError from cubicweb import transaction as tx, server, neg_role from cubicweb.schema import VIRTUAL_RTYPES from cubicweb.cwconfig import CubicWebNoAppConfiguration @@ -666,6 +667,16 @@ self.critical('transaction has been rollbacked') except: pass + if ex.__class__.__name__ == 'IntegrityError': + # need string comparison because of various backends + for arg in ex.args: + mo = re.search('unique_cw_[^ ]+_idx', arg) + if mo is not None: + index_name = mo.group(0) + elements = index_name.rstrip('_idx').split('_cw_')[1:] + etype = elements[0] + rtypes = elements[1:] + raise UniqueTogetherError(etype, rtypes) raise return cursor