2
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
2
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
4
4
# This module is part of SQLAlchemy and is released under
5
5
# the MIT License: http://www.opensource.org/licenses/mit-license.php
15
from sqlalchemy import util, event
16
from sqlalchemy.util import topological
17
from sqlalchemy.orm import attributes, interfaces, persistence
18
from sqlalchemy.orm import util as mapperutil
19
session = util.importlater("sqlalchemy.orm", "session")
15
from .. import util, event
16
from ..util import topological
17
from . import attributes, persistence, util as orm_util
19
sessionlib = util.importlater("sqlalchemy.orm", "session")
21
22
def track_cascade_events(descriptor, prop):
22
23
"""Establish event listeners on object attributes which handle
29
30
# process "save_update" cascade rules for when
30
31
# an instance is appended to the list of another instance
32
sess = session._state_session(state)
36
sess = sessionlib._state_session(state)
38
if sess._warn_on_events:
39
sess._flush_warning("collection append")
34
41
prop = state.manager.mapper._props[key]
35
42
item_state = attributes.instance_state(item)
36
if prop.cascade.save_update and \
43
if prop._cascade.save_update and \
37
44
(prop.cascade_backrefs or key == initiator.key) and \
38
not sess._contains_state(item_state):
45
not sess._contains_state(item_state):
39
46
sess._save_or_update_state(item_state)
42
49
def remove(state, item, initiator):
43
sess = session._state_session(state)
53
sess = sessionlib._state_session(state)
45
56
prop = state.manager.mapper._props[key]
58
if sess._warn_on_events:
62
else "related attribute delete")
46
64
# expunge pending orphans
47
65
item_state = attributes.instance_state(item)
48
if prop.cascade.delete_orphan and \
66
if prop._cascade.delete_orphan and \
49
67
item_state in sess._new and \
50
prop.mapper._is_orphan(item_state):
68
prop.mapper._is_orphan(item_state):
53
71
def set_(state, newvalue, oldvalue, initiator):
56
74
if oldvalue is newvalue:
59
sess = session._state_session(state)
77
sess = sessionlib._state_session(state)
80
if sess._warn_on_events:
81
sess._flush_warning("related attribute set")
61
83
prop = state.manager.mapper._props[key]
62
84
if newvalue is not None:
63
85
newvalue_state = attributes.instance_state(newvalue)
64
if prop.cascade.save_update and \
86
if prop._cascade.save_update and \
65
87
(prop.cascade_backrefs or key == initiator.key) and \
66
88
not sess._contains_state(newvalue_state):
67
89
sess._save_or_update_state(newvalue_state)
69
91
if oldvalue is not None and \
70
92
oldvalue is not attributes.PASSIVE_NO_RESULT and \
71
prop.cascade.delete_orphan:
93
prop._cascade.delete_orphan:
72
94
# possible to reach here with attributes.NEVER_SET ?
73
95
oldvalue_state = attributes.instance_state(oldvalue)
155
177
def get_attribute_history(self, state, key,
156
178
passive=attributes.PASSIVE_NO_INITIALIZE):
157
"""facade to attributes.get_state_history(), including caching of results."""
179
"""facade to attributes.get_state_history(), including
180
caching of results."""
159
182
hashkey = ("history", state, key)
166
189
history, state_history, cached_passive = self.attributes[hashkey]
167
190
# if the cached lookup was "passive" and now
168
191
# we want non-passive, do a non-passive lookup and re-cache
169
if cached_passive is not attributes.PASSIVE_OFF \
170
and passive is attributes.PASSIVE_OFF:
193
if not cached_passive & attributes.SQL_OK \
194
and passive & attributes.SQL_OK:
171
195
impl = state.manager[key].impl
172
196
history = impl.get_history(state, state.dict,
173
attributes.PASSIVE_OFF)
197
attributes.PASSIVE_OFF |
198
attributes.LOAD_AGAINST_COMMITTED)
174
199
if history and impl.uses_objects:
175
200
state_history = history.as_state()
180
205
impl = state.manager[key].impl
181
206
# TODO: store the history as (state, object) tuples
182
207
# so we don't have to keep converting here
183
history = impl.get_history(state, state.dict, passive)
208
history = impl.get_history(state, state.dict, passive |
209
attributes.LOAD_AGAINST_COMMITTED)
184
210
if history and impl.uses_objects:
185
211
state_history = history.as_state()
187
213
state_history = history
188
self.attributes[hashkey] = (history, state_history, passive)
214
self.attributes[hashkey] = (history, state_history,
190
217
return state_history
204
231
if not state.deleted and operation is not None:
205
232
util.warn("Object of type %s not in session, %s operation "
206
233
"along '%s' will not proceed" %
207
(mapperutil.state_class_str(state), operation, prop))
234
(orm_util.state_class_str(state), operation, prop))
210
237
if state not in self.states:
211
238
mapper = state.manager.mapper
213
240
if mapper not in self.mappers:
214
mapper._per_mapper_flush_actions(self)
241
self._per_mapper_flush_actions(mapper)
216
243
self.mappers[mapper].add(state)
217
244
self.states[state] = (isdelete, listonly)
226
253
states.add(state)
227
254
cols.update(post_update_cols)
256
def _per_mapper_flush_actions(self, mapper):
257
saves = SaveUpdateAll(self, mapper.base_mapper)
258
deletes = DeleteAll(self, mapper.base_mapper)
259
self.dependencies.add((saves, deletes))
261
for dep in mapper._dependency_processors:
262
dep.per_property_preprocessors(self)
264
for prop in mapper.relationships:
267
dep = prop._dependency_processor
268
dep.per_property_preprocessors(self)
229
270
@util.memoized_property
230
271
def _mapper_for_dep(self):
231
272
"""return a dynamic mapping of (Mapper, DependencyProcessor) to
239
280
return util.PopulateDict(
240
lambda tup:tup[0]._props.get(tup[1].key) is tup[1].prop
281
lambda tup: tup[0]._props.get(tup[1].key) is tup[1].prop
243
284
def filter_states_for_dep(self, dep, states):
330
371
postsort_actions):
331
372
rec.execute(self)
334
374
def finalize_flush_changes(self):
335
"""mark processed objects as clean / deleted after a successful flush().
375
"""mark processed objects as clean / deleted after a successful
337
378
this method is called within the flush() method after the
338
379
execute() method has succeeded and the transaction has been committed.
341
for state, (isdelete, listonly) in self.states.iteritems():
343
self.session._remove_newly_deleted(state)
346
# debug... would like to see how many do this
347
self.session._register_newly_persistent(state)
382
states = set(self.states)
384
s for (s, (isdelete, listonly)) in self.states.iteritems()
387
other = states.difference(isdel)
388
self.session._remove_newly_deleted(isdel)
389
self.session._register_newly_persistent(other)
349
392
class IterateMappersMixin(object):
350
393
def _mappers(self, uow):
351
394
if self.fromparent:
353
m for m in self.dependency_processor.parent.self_and_descendants
397
self.dependency_processor.parent.self_and_descendants
354
398
if uow._mapper_for_dep[(m, self.dependency_processor)]
357
401
return self.dependency_processor.mapper.self_and_descendants
359
404
class Preprocess(IterateMappersMixin):
360
405
def __init__(self, dependency_processor, fromparent):
361
406
self.dependency_processor = dependency_processor
418
464
",".join(str(x) for x in self.__dict__.values())
421
468
class ProcessAll(IterateMappersMixin, PostSortRec):
422
469
def __init__(self, uow, dependency_processor, delete, fromparent):
423
470
self.dependency_processor = dependency_processor
424
471
self.delete = delete
425
472
self.fromparent = fromparent
426
uow.deps[dependency_processor.parent.base_mapper].add(dependency_processor)
473
uow.deps[dependency_processor.parent.base_mapper].\
474
add(dependency_processor)
428
476
def execute(self, uow):
429
477
states = self._elements(uow)
453
501
if isdelete == self.delete and not listonly:
456
505
class IssuePostUpdate(PostSortRec):
457
506
def __init__(self, uow, mapper, isdelete):
458
507
self.mapper = mapper
465
514
persistence.post_update(self.mapper, states, uow, cols)
467
517
class SaveUpdateAll(PostSortRec):
468
518
def __init__(self, uow, mapper):
469
519
self.mapper = mapper
478
528
def per_state_flush_actions(self, uow):
479
states = list(uow.states_for_mapper_hierarchy(self.mapper, False, False))
480
for rec in self.mapper._per_state_flush_actions(
529
states = list(uow.states_for_mapper_hierarchy(
530
self.mapper, False, False))
531
base_mapper = self.mapper.base_mapper
532
delete_all = DeleteAll(uow, base_mapper)
534
# keep saves before deletes -
535
# this ensures 'row switch' operations work
536
action = SaveUpdateState(uow, state, base_mapper)
537
uow.dependencies.add((action, delete_all))
486
540
for dep in uow.deps[self.mapper]:
487
541
states_for_prop = uow.filter_states_for_dep(dep, states)
488
542
dep.per_state_flush_actions(uow, states_for_prop, False)
490
545
class DeleteAll(PostSortRec):
491
546
def __init__(self, uow, mapper):
492
547
self.mapper = mapper
501
556
def per_state_flush_actions(self, uow):
502
states = list(uow.states_for_mapper_hierarchy(self.mapper, True, False))
503
for rec in self.mapper._per_state_flush_actions(
557
states = list(uow.states_for_mapper_hierarchy(
558
self.mapper, True, False))
559
base_mapper = self.mapper.base_mapper
560
save_all = SaveUpdateAll(uow, base_mapper)
562
# keep saves before deletes -
563
# this ensures 'row switch' operations work
564
action = DeleteState(uow, state, base_mapper)
565
uow.dependencies.add((save_all, action))
509
568
for dep in uow.deps[self.mapper]:
510
569
states_for_prop = uow.filter_states_for_dep(dep, states)
511
570
dep.per_state_flush_actions(uow, states_for_prop, True)
513
573
class ProcessState(PostSortRec):
514
574
def __init__(self, uow, dependency_processor, delete, state):
515
575
self.dependency_processor = dependency_processor
535
595
return "%s(%s, %s, delete=%s)" % (
536
596
self.__class__.__name__,
537
597
self.dependency_processor,
538
mapperutil.state_str(self.state),
598
orm_util.state_str(self.state),
542
603
class SaveUpdateState(PostSortRec):
543
604
def __init__(self, uow, state, mapper):
544
605
self.state = state
559
620
def __repr__(self):
560
621
return "%s(%s)" % (
561
622
self.__class__.__name__,
562
mapperutil.state_str(self.state)
623
orm_util.state_str(self.state)
565
627
class DeleteState(PostSortRec):
566
628
def __init__(self, uow, state, mapper):
567
629
self.state = state