nicer error reporting for unique together constraints stable
authorAlexandre Fayolle <alexandre.fayolle@logilab.fr>
Fri, 10 Sep 2010 18:30:38 +0200
branchstable
changeset 6211 e9d125fd1465
parent 6210 d36104de8459
child 6212 73565b770559
nicer error reporting for unique together constraints
_exceptions.py
server/repository.py
server/sources/native.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 #########################################################
 
--- 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:
--- 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