48 |
48 |
49 from cubicweb import (CW_SOFTWARE_ROOT, CW_MIGRATION_MAP, QueryError, |
49 from cubicweb import (CW_SOFTWARE_ROOT, CW_MIGRATION_MAP, QueryError, |
50 UnknownEid, AuthenticationError, ExecutionError, |
50 UnknownEid, AuthenticationError, ExecutionError, |
51 ETypeNotSupportedBySources, MultiSourcesError, |
51 ETypeNotSupportedBySources, MultiSourcesError, |
52 BadConnectionId, Unauthorized, ValidationError, |
52 BadConnectionId, Unauthorized, ValidationError, |
53 RepositoryError, typed_eid, onevent) |
53 RepositoryError, UniqueTogetherError, typed_eid, onevent) |
54 from cubicweb import cwvreg, schema, server |
54 from cubicweb import cwvreg, schema, server |
55 from cubicweb.server import utils, hook, pool, querier, sources |
55 from cubicweb.server import utils, hook, pool, querier, sources |
56 from cubicweb.server.session import Session, InternalSession, InternalManager, \ |
56 from cubicweb.server.session import Session, InternalSession, InternalManager, \ |
57 security_enabled |
57 security_enabled |
58 from cubicweb.server.ssplanner import EditedEntity |
58 from cubicweb.server.ssplanner import EditedEntity |
59 |
|
60 |
59 |
61 def del_existing_rel_if_needed(session, eidfrom, rtype, eidto): |
60 def del_existing_rel_if_needed(session, eidfrom, rtype, eidto): |
62 """delete existing relation when adding a new one if card is 1 or ? |
61 """delete existing relation when adding a new one if card is 1 or ? |
63 |
62 |
64 have to be done once the new relation has been inserted to avoid having |
63 have to be done once the new relation has been inserted to avoid having |
80 # same transaction where the entity is being created. This never occurs from |
79 # same transaction where the entity is being created. This never occurs from |
81 # the web interface but may occurs during test or dbapi connection (though |
80 # the web interface but may occurs during test or dbapi connection (though |
82 # not expected for this). So: don't do it, we pretend to ensure repository |
81 # not expected for this). So: don't do it, we pretend to ensure repository |
83 # consistency. |
82 # consistency. |
84 # |
83 # |
85 # XXX we don't want read permissions to be applied but we want delete |
84 # notes: |
86 # permission to be checked |
85 # * inlined relations will be implicitly deleted for the subject entity |
87 rschema = session.repo.schema.rschema(rtype) |
86 # * we don't want read permissions to be applied but we want delete |
88 if card[0] in '1?': |
87 # permission to be checked |
89 if not rschema.inlined: # inlined relations will be implicitly deleted |
88 if card[0] in '1?' and not session.repo.schema.rschema(rtype).inlined: |
90 with security_enabled(session, read=False): |
89 with security_enabled(session, read=False): |
91 session.execute('DELETE X %s Y WHERE X eid %%(x)s, ' |
90 session.execute('DELETE X %s Y WHERE X eid %%(x)s, ' |
92 'NOT Y eid %%(y)s' % rtype, |
91 'NOT Y eid %%(y)s' % rtype, |
93 {'x': eidfrom, 'y': eidto}) |
92 {'x': eidfrom, 'y': eidto}) |
94 if card[1] in '1?': |
93 if card[1] in '1?': |
95 with security_enabled(session, read=False): |
94 with security_enabled(session, read=False): |
96 session.execute('DELETE X %s Y WHERE Y eid %%(y)s, ' |
95 session.execute('DELETE X %s Y WHERE Y eid %%(y)s, ' |
97 'NOT X eid %%(x)s' % rtype, |
96 'NOT X eid %%(x)s' % rtype, |
1068 if not rschema.final: # inlined relation |
1067 if not rschema.final: # inlined relation |
1069 relations.append((attr, edited[attr])) |
1068 relations.append((attr, edited[attr])) |
1070 edited.set_defaults() |
1069 edited.set_defaults() |
1071 if session.is_hook_category_activated('integrity'): |
1070 if session.is_hook_category_activated('integrity'): |
1072 edited.check(creation=True) |
1071 edited.check(creation=True) |
1073 source.add_entity(session, entity) |
1072 try: |
|
1073 source.add_entity(session, entity) |
|
1074 except UniqueTogetherError, exc: |
|
1075 etype, rtypes = exc.args |
|
1076 problems = {} |
|
1077 for col in rtypes: |
|
1078 problems[col] = session._('violates unique_together constraints (%s)') % (','.join(rtypes)) |
|
1079 raise ValidationError(entity.eid, problems) |
|
1080 self.add_info(session, entity, source, extid, complete=False) |
1074 edited.saved = True |
1081 edited.saved = True |
1075 self.add_info(session, entity, source, extid, complete=False) |
|
1076 entity._cw_is_saved = True # entity has an eid and is saved |
|
1077 # prefill entity relation caches |
1082 # prefill entity relation caches |
1078 for rschema in eschema.subject_relations(): |
1083 for rschema in eschema.subject_relations(): |
1079 rtype = str(rschema) |
1084 rtype = str(rschema) |
1080 if rtype in schema.VIRTUAL_RTYPES: |
1085 if rtype in schema.VIRTUAL_RTYPES: |
1081 continue |
1086 continue |
1087 for rschema in eschema.object_relations(): |
1092 for rschema in eschema.object_relations(): |
1088 rtype = str(rschema) |
1093 rtype = str(rschema) |
1089 if rtype in schema.VIRTUAL_RTYPES: |
1094 if rtype in schema.VIRTUAL_RTYPES: |
1090 continue |
1095 continue |
1091 entity.cw_set_relation_cache(rtype, 'object', session.empty_rset()) |
1096 entity.cw_set_relation_cache(rtype, 'object', session.empty_rset()) |
1092 # set inline relation cache before call to after_add_entity |
1097 # set inlined relation cache before call to after_add_entity |
1093 for attr, value in relations: |
1098 for attr, value in relations: |
1094 session.update_rel_cache_add(entity.eid, attr, value) |
1099 session.update_rel_cache_add(entity.eid, attr, value) |
|
1100 del_existing_rel_if_needed(session, entity.eid, attr, value) |
1095 # trigger after_add_entity after after_add_relation |
1101 # trigger after_add_entity after after_add_relation |
1096 if source.should_call_hooks: |
1102 if source.should_call_hooks: |
1097 self.hm.call_hooks('after_add_entity', session, entity=entity) |
1103 self.hm.call_hooks('after_add_entity', session, entity=entity) |
1098 # call hooks for inlined relations |
1104 # call hooks for inlined relations |
1099 for attr, value in relations: |
1105 for attr, value in relations: |
1140 eidfrom=entity.eid, rtype=attr, |
1146 eidfrom=entity.eid, rtype=attr, |
1141 eidto=previous_value) |
1147 eidto=previous_value) |
1142 relations.append((attr, edited[attr], previous_value)) |
1148 relations.append((attr, edited[attr], previous_value)) |
1143 if source.should_call_hooks: |
1149 if source.should_call_hooks: |
1144 # call hooks for inlined relations |
1150 # call hooks for inlined relations |
1145 for attr, value, _ in relations: |
1151 for attr, value, _t in relations: |
1146 hm.call_hooks('before_add_relation', session, |
1152 hm.call_hooks('before_add_relation', session, |
1147 eidfrom=entity.eid, rtype=attr, eidto=value) |
1153 eidfrom=entity.eid, rtype=attr, eidto=value) |
1148 if not only_inline_rels: |
1154 if not only_inline_rels: |
1149 hm.call_hooks('before_update_entity', session, entity=entity) |
1155 hm.call_hooks('before_update_entity', session, entity=entity) |
1150 if session.is_hook_category_activated('integrity'): |
1156 if session.is_hook_category_activated('integrity'): |
1151 edited.check() |
1157 edited.check() |
1152 source.update_entity(session, entity) |
1158 try: |
1153 edited.saved = True |
1159 source.update_entity(session, entity) |
|
1160 edited.saved = True |
|
1161 except UniqueTogetherError, exc: |
|
1162 etype, rtypes = exc.args |
|
1163 problems = {} |
|
1164 for col in rtypes: |
|
1165 problems[col] = session._('violates unique_together constraints (%s)') % (','.join(rtypes)) |
|
1166 raise ValidationError(entity.eid, problems) |
1154 self.system_source.update_info(session, entity, need_fti_update) |
1167 self.system_source.update_info(session, entity, need_fti_update) |
1155 if source.should_call_hooks: |
1168 if source.should_call_hooks: |
1156 if not only_inline_rels: |
1169 if not only_inline_rels: |
1157 hm.call_hooks('after_update_entity', session, entity=entity) |
1170 hm.call_hooks('after_update_entity', session, entity=entity) |
1158 for attr, value, prevvalue in relations: |
1171 for attr, value, prevvalue in relations: |