📄 session.py
字号:
def _cascade_save_or_update(self, instance): for obj, mapper in _cascade_iterator('save-update', instance, halt_on=lambda c:c in self): self._save_or_update_impl(obj, mapper.entity_name) def delete(self, instance): """Mark the given instance as deleted. The delete operation occurs upon ``flush()``. """ self._delete_impl(instance) for c, m in _cascade_iterator('delete', instance): self._delete_impl(c, ignore_transient=True) def merge(self, instance, entity_name=None, dont_load=False, _recursive=None): """Copy the state of the given `instance` onto the persistent instance with the same identifier. If there is no persistent instance currently associated with the session, it will be loaded. Return the persistent instance. If the given instance is unsaved, save a copy of and return it as a newly persistent instance. The given instance does not become associated with the session. This operation cascades to associated instances if the association is mapped with ``cascade="merge"``. """ if _recursive is None: _recursive = {} # TODO: this should be an IdentityDict for instances, but will need a separate # dict for PropertyLoader tuples if entity_name is not None: mapper = _class_mapper(instance.__class__, entity_name=entity_name) else: mapper = _object_mapper(instance) if instance in _recursive: return _recursive[instance] key = getattr(instance, '_instance_key', None) if key is None: if dont_load: raise exceptions.InvalidRequestError("merge() with dont_load=True option does not support objects transient (i.e. unpersisted) objects. flush() all changes on mapped instances before merging with dont_load=True.") merged = attributes.new_instance(mapper.class_) else: if key in self.identity_map: merged = self.identity_map[key] elif dont_load: if instance._state.modified: raise exceptions.InvalidRequestError("merge() with dont_load=True option does not support objects marked as 'dirty'. flush() all changes on mapped instances before merging with dont_load=True.") merged = attributes.new_instance(mapper.class_) merged._instance_key = key merged._entity_name = entity_name self._update_impl(merged, entity_name=mapper.entity_name) else: merged = self.get(mapper.class_, key[1]) if merged is None: raise exceptions.AssertionError("Instance %s has an instance key but is not persisted" % mapperutil.instance_str(instance)) _recursive[instance] = merged for prop in mapper.iterate_properties: prop.merge(self, instance, merged, dont_load, _recursive) if key is None: self.save(merged, entity_name=mapper.entity_name) elif dont_load: merged._state.commit_all() return merged def identity_key(cls, *args, **kwargs): """Get an identity key. Valid call signatures: * ``identity_key(class, ident, entity_name=None)`` class mapped class (must be a positional argument) ident primary key, if the key is composite this is a tuple entity_name optional entity name * ``identity_key(instance=instance)`` instance object instance (must be given as a keyword arg) * ``identity_key(class, row=row, entity_name=None)`` class mapped class (must be a positional argument) row result proxy row (must be given as a keyword arg) entity_name optional entity name (must be given as a keyword arg) """ if args: if len(args) == 1: class_ = args[0] try: row = kwargs.pop("row") except KeyError: ident = kwargs.pop("ident") entity_name = kwargs.pop("entity_name", None) elif len(args) == 2: class_, ident = args entity_name = kwargs.pop("entity_name", None) elif len(args) == 3: class_, ident, entity_name = args else: raise exceptions.ArgumentError("expected up to three " "positional arguments, got %s" % len(args)) if kwargs: raise exceptions.ArgumentError("unknown keyword arguments: %s" % ", ".join(kwargs.keys())) mapper = _class_mapper(class_, entity_name=entity_name) if "ident" in locals(): return mapper.identity_key_from_primary_key(ident) return mapper.identity_key_from_row(row) instance = kwargs.pop("instance") if kwargs: raise exceptions.ArgumentError("unknown keyword arguments: %s" % ", ".join(kwargs.keys())) mapper = _object_mapper(instance) return mapper.identity_key_from_instance(instance) identity_key = classmethod(identity_key) def object_session(cls, instance): """Return the ``Session`` to which the given object belongs.""" return object_session(instance) object_session = classmethod(object_session) def _save_impl(self, instance, **kwargs): if hasattr(instance, '_instance_key'): raise exceptions.InvalidRequestError("Instance '%s' is already persistent" % mapperutil.instance_str(instance)) else: # TODO: consolidate the steps here attributes.manage(instance) instance._entity_name = kwargs.get('entity_name', None) self._attach(instance) self.uow.register_new(instance) def _update_impl(self, instance, **kwargs): if instance in self and instance not in self.deleted: return if not hasattr(instance, '_instance_key'): raise exceptions.InvalidRequestError("Instance '%s' is not persisted" % mapperutil.instance_str(instance)) elif self.identity_map.get(instance._instance_key, instance) is not instance: raise exceptions.InvalidRequestError("Could not update instance '%s', identity key %s; a different instance with the same identity key already exists in this session." % (mapperutil.instance_str(instance), instance._instance_key)) self._attach(instance) def _save_or_update_impl(self, instance, entity_name=None): key = getattr(instance, '_instance_key', None) if key is None: self._save_impl(instance, entity_name=entity_name) else: self._update_impl(instance, entity_name=entity_name) def _delete_impl(self, instance, ignore_transient=False): if instance in self and instance in self.deleted: return if not hasattr(instance, '_instance_key'): if ignore_transient: return else: raise exceptions.InvalidRequestError("Instance '%s' is not persisted" % mapperutil.instance_str(instance)) if self.identity_map.get(instance._instance_key, instance) is not instance: raise exceptions.InvalidRequestError("Instance '%s' is with key %s already persisted with a different identity" % (mapperutil.instance_str(instance), instance._instance_key)) self._attach(instance) self.uow.register_deleted(instance) def _attach(self, instance): old_id = getattr(instance, '_sa_session_id', None) if old_id != self.hash_key: if old_id is not None and old_id in _sessions and instance in _sessions[old_id]: raise exceptions.InvalidRequestError("Object '%s' is already attached " "to session '%s' (this is '%s')" % (mapperutil.instance_str(instance), old_id, id(self))) key = getattr(instance, '_instance_key', None) if key is not None: self.identity_map[key] = instance instance._sa_session_id = self.hash_key def _unattach(self, instance): if instance._sa_session_id == self.hash_key: del instance._sa_session_id def _validate_persistent(self, instance): """Validate that the given instance is persistent within this ``Session``. """ return instance in self def __contains__(self, instance): """Return True if the given instance is associated with this session. The instance may be pending or persistent within the Session for a result of True. """ return instance._state in self.uow.new or (hasattr(instance, '_instance_key') and self.identity_map.get(instance._instance_key) is instance) def __iter__(self): """Return an iterator of all instances which are pending or persistent within this Session.""" return iter(list(self.uow.new.values()) + self.uow.identity_map.values()) def is_modified(self, instance, include_collections=True, passive=False): """Return True if the given instance has modified attributes. This method retrieves a history instance for each instrumented attribute on the instance and performs a comparison of the current value to its previously committed value. Note that instances present in the 'dirty' collection may result in a value of ``False`` when tested with this method. `include_collections` indicates if multivalued collections should be included in the operation. Setting this to False is a way to detect only local-column based properties (i.e. scalar columns or many-to-one foreign keys) that would result in an UPDATE for this instance upon flush. The `passive` flag indicates if unloaded attributes and collections should not be loaded in the course of performing this test. """ for attr in attributes._managed_attributes(instance.__class__): if not include_collections and hasattr(attr.impl, 'get_collection'): continue (added, unchanged, deleted) = attr.get_history(instance) if added or deleted: return True return False def dirty(self): """Return a ``Set`` of all instances marked as 'dirty' within this ``Session``. Note that the 'dirty' state here is 'optimistic'; most attribute-setting or collection modification operations will mark an instance as 'dirty' and place it in this set, even if there is no net change to the attribute's value. At flush time, the value of each attribute is compared to its previously saved value, and if there's no net change, no SQL operation will occur (this is a more expensive operation so it's only done at flush time). To check if an instance has actionable net changes to its attributes, use the is_modified() method. """ return self.uow.locate_dirty() dirty = property(dirty) def deleted(self): "Return a ``Set`` of all instances marked as 'deleted' within this ``Session``" return util.IdentitySet(self.uow.deleted.values()) deleted = property(deleted) def new(self): "Return a ``Set`` of all instances marked as 'new' within this ``Session``." return util.IdentitySet(self.uow.new.values()) new = property(new)def _expire_state(state, attribute_names): """Standalone expire instance function. Installs a callable with the given instance's _state which will fire off when any of the named attributes are accessed; their existing value is removed. If the list is None or blank, the entire instance is expired. """ state.expire_attributes(attribute_names)register_attribute = unitofwork.register_attribute_sessions = weakref.WeakValueDictionary()def _cascade_iterator(cascade, instance, **kwargs): mapper = _object_mapper(instance) for (o, m) in mapper.cascade_iterator(cascade, instance._state, **kwargs): yield o, mdef object_session(instance): """Return the ``Session`` to which the given instance is bound, or ``None`` if none.""" hashkey = getattr(instance, '_sa_session_id', None) if hashkey is not None: sess = _sessions.get(hashkey) if sess is not None and instance in sess: return sess return None# Lazy initialization to avoid circular importsunitofwork.object_session = object_sessionfrom sqlalchemy.orm import mappermapper._expire_state = _expire_state
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -