| 3517 | return wrapper |
| 3518 | @cut_traceback |
| 3519 | def remove(wrapper, items): |
| 3520 | obj = wrapper._obj_ |
| 3521 | attr = wrapper._attr_ |
| 3522 | cache = obj._session_cache_ |
| 3523 | if cache is None or not cache.is_alive: throw_db_session_is_over('change collection', obj, attr) |
| 3524 | if obj._status_ in del_statuses: throw_object_was_deleted(obj) |
| 3525 | with cache.flush_disabled(): |
| 3526 | reverse = attr.reverse |
| 3527 | if not reverse: throw(NotImplementedError) |
| 3528 | items = attr.validate(items, obj) |
| 3529 | setdata = obj._vals_.get(attr) |
| 3530 | if setdata is not None and setdata.removed: |
| 3531 | items -= setdata.removed |
| 3532 | if not items: return |
| 3533 | if setdata is None or not setdata.is_fully_loaded: |
| 3534 | setdata = attr.load(obj, items) |
| 3535 | items &= setdata |
| 3536 | undo_funcs = [] |
| 3537 | try: |
| 3538 | if not reverse.is_collection: |
| 3539 | if attr.cascade_delete: |
| 3540 | for item in items: item._delete_(undo_funcs) |
| 3541 | else: |
| 3542 | for item in items: reverse.__set__(item, None, undo_funcs) |
| 3543 | else: reverse.reverse_remove(items, obj, undo_funcs) |
| 3544 | except: |
| 3545 | for undo_func in reversed(undo_funcs): undo_func() |
| 3546 | raise |
| 3547 | setdata -= items |
| 3548 | if setdata.count is not None: setdata.count -= len(items) |
| 3549 | added = setdata.added |
| 3550 | removed = setdata.removed |
| 3551 | if added: (items, setdata.added) = (items - added, added - items) |
| 3552 | if removed: removed |= items |
| 3553 | else: setdata.removed = items # removed may be None |
| 3554 | |
| 3555 | cache.modified_collections[attr].add(obj) |
| 3556 | cache.modified = True |
| 3557 | @cut_traceback |
| 3558 | def __isub__(wrapper, items): |
| 3559 | wrapper.remove(items) |