~ubuntu-branches/debian/jessie/sqlalchemy/jessie

« back to all changes in this revision

Viewing changes to lib/sqlalchemy/orm/unitofwork.py

  • Committer: Package Import Robot
  • Author(s): Piotr Ożarowski, Jakub Wilk, Piotr Ożarowski
  • Date: 2013-07-06 20:53:52 UTC
  • mfrom: (1.4.23) (16.1.17 experimental)
  • Revision ID: package-import@ubuntu.com-20130706205352-ryppl1eto3illd79
Tags: 0.8.2-1
[ Jakub Wilk ]
* Use canonical URIs for Vcs-* fields.

[ Piotr Ożarowski ]
* New upstream release
* Upload to unstable
* Build depend on python3-all instead of -dev, extensions are not built for
  Python 3.X 

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# orm/unitofwork.py
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>
3
3
#
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
12
12
 
13
13
"""
14
14
 
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
 
18
 
 
19
sessionlib = util.importlater("sqlalchemy.orm", "session")
 
20
 
20
21
 
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
31
32
 
32
 
        sess = session._state_session(state)
 
33
        if item is None:
 
34
            return
 
35
 
 
36
        sess = sessionlib._state_session(state)
33
37
        if sess:
 
38
            if sess._warn_on_events:
 
39
                sess._flush_warning("collection append")
 
40
 
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)
40
47
        return item
41
48
 
42
49
    def remove(state, item, initiator):
43
 
        sess = session._state_session(state)
 
50
        if item is None:
 
51
            return
 
52
 
 
53
        sess = sessionlib._state_session(state)
44
54
        if sess:
 
55
 
45
56
            prop = state.manager.mapper._props[key]
 
57
 
 
58
            if sess._warn_on_events:
 
59
                sess._flush_warning(
 
60
                        "collection remove"
 
61
                        if prop.uselist
 
62
                        else "related attribute delete")
 
63
 
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):
51
69
                    sess.expunge(item)
52
70
 
53
71
    def set_(state, newvalue, oldvalue, initiator):
56
74
        if oldvalue is newvalue:
57
75
            return newvalue
58
76
 
59
 
        sess = session._state_session(state)
 
77
        sess = sessionlib._state_session(state)
60
78
        if sess:
 
79
 
 
80
            if sess._warn_on_events:
 
81
                sess._flush_warning("related attribute set")
 
82
 
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)
68
90
 
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)
74
96
 
154
176
 
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."""
158
181
 
159
182
        hashkey = ("history", state, key)
160
183
 
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:
 
192
 
 
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()
176
201
                else:
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()
186
212
            else:
187
213
                state_history = history
188
 
            self.attributes[hashkey] = (history, state_history, passive)
 
214
            self.attributes[hashkey] = (history, state_history,
 
215
                    passive)
189
216
 
190
217
        return state_history
191
218
 
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))
208
235
            return False
209
236
 
210
237
        if state not in self.states:
211
238
            mapper = state.manager.mapper
212
239
 
213
240
            if mapper not in self.mappers:
214
 
                mapper._per_mapper_flush_actions(self)
 
241
                self._per_mapper_flush_actions(mapper)
215
242
 
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)
228
255
 
 
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))
 
260
 
 
261
        for dep in mapper._dependency_processors:
 
262
            dep.per_property_preprocessors(self)
 
263
 
 
264
        for prop in mapper.relationships:
 
265
            if prop.viewonly:
 
266
                continue
 
267
            dep = prop._dependency_processor
 
268
            dep.per_property_preprocessors(self)
 
269
 
229
270
    @util.memoized_property
230
271
    def _mapper_for_dep(self):
231
272
        """return a dynamic mapping of (Mapper, DependencyProcessor) to
237
278
 
238
279
        """
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
241
282
                )
242
283
 
243
284
    def filter_states_for_dep(self, dep, states):
330
371
                                    postsort_actions):
331
372
                rec.execute(self)
332
373
 
333
 
 
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
 
376
        flush().
336
377
 
337
378
        this method is called within the flush() method after the
338
379
        execute() method has succeeded and the transaction has been committed.
339
380
 
340
381
        """
341
 
        for state, (isdelete, listonly) in self.states.iteritems():
342
 
            if isdelete:
343
 
                self.session._remove_newly_deleted(state)
344
 
            else:
345
 
                # if listonly:
346
 
                #   debug... would like to see how many do this
347
 
                self.session._register_newly_persistent(state)
 
382
        states = set(self.states)
 
383
        isdel = set(
 
384
            s for (s, (isdelete, listonly)) in self.states.iteritems()
 
385
            if isdelete
 
386
        )
 
387
        other = states.difference(isdel)
 
388
        self.session._remove_newly_deleted(isdel)
 
389
        self.session._register_newly_persistent(other)
 
390
 
348
391
 
349
392
class IterateMappersMixin(object):
350
393
    def _mappers(self, uow):
351
394
        if self.fromparent:
352
395
            return iter(
353
 
                m for m in self.dependency_processor.parent.self_and_descendants
 
396
                m for m in
 
397
                self.dependency_processor.parent.self_and_descendants
354
398
                if uow._mapper_for_dep[(m, self.dependency_processor)]
355
399
            )
356
400
        else:
357
401
            return self.dependency_processor.mapper.self_and_descendants
358
402
 
 
403
 
359
404
class Preprocess(IterateMappersMixin):
360
405
    def __init__(self, dependency_processor, fromparent):
361
406
        self.dependency_processor = dependency_processor
396
441
        else:
397
442
            return False
398
443
 
 
444
 
399
445
class PostSortRec(object):
400
446
    disabled = False
401
447
 
418
464
            ",".join(str(x) for x in self.__dict__.values())
419
465
        )
420
466
 
 
467
 
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)
427
475
 
428
476
    def execute(self, uow):
429
477
        states = self._elements(uow)
453
501
                if isdelete == self.delete and not listonly:
454
502
                    yield state
455
503
 
 
504
 
456
505
class IssuePostUpdate(PostSortRec):
457
506
    def __init__(self, uow, mapper, isdelete):
458
507
        self.mapper = mapper
464
513
 
465
514
        persistence.post_update(self.mapper, states, uow, cols)
466
515
 
 
516
 
467
517
class SaveUpdateAll(PostSortRec):
468
518
    def __init__(self, uow, mapper):
469
519
        self.mapper = mapper
476
526
        )
477
527
 
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(
481
 
                            uow,
482
 
                            states,
483
 
                            False):
484
 
            yield rec
 
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)
 
533
        for state in states:
 
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))
 
538
            yield action
485
539
 
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)
489
543
 
 
544
 
490
545
class DeleteAll(PostSortRec):
491
546
    def __init__(self, uow, mapper):
492
547
        self.mapper = mapper
499
554
        )
500
555
 
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(
504
 
                            uow,
505
 
                            states,
506
 
                            True):
507
 
            yield rec
 
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)
 
561
        for state in states:
 
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))
 
566
            yield action
508
567
 
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)
512
571
 
 
572
 
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),
539
599
            self.delete
540
600
        )
541
601
 
 
602
 
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)
563
624
        )
564
625
 
 
626
 
565
627
class DeleteState(PostSortRec):
566
628
    def __init__(self, uow, state, mapper):
567
629
        self.state = state
582
644
    def __repr__(self):
583
645
        return "%s(%s)" % (
584
646
            self.__class__.__name__,
585
 
            mapperutil.state_str(self.state)
 
647
            orm_util.state_str(self.state)
586
648
        )
587