def get_bind(self, mapper, clause=None, **kwargs): """Return an engine corresponding to the given arguments. mapper mapper relative to the desired operation clause a ClauseElement which is to be executed. if mapper is not present, this may be used to locate Table objects, which are then associated with mappers which have associated binds. \**kwargs Subclasses (i.e. ShardedSession) may add additional arguments to get_bind() which are passed through here. """ if mapper is None and clause is None: if self.bind is not None: return self.bind else: raise exceptions.UnboundExecutionError( "This session is unbound to any Engine or Connection; specify a mapper to get_bind()" ) elif len(self.__binds): if mapper is not None: if isinstance(mapper, type): mapper = _class_mapper(mapper) if mapper.base_mapper in self.__binds: return self.__binds[mapper.base_mapper] elif mapper.compile().mapped_table in self.__binds: return self.__binds[mapper.mapped_table] if clause is not None: for t in clause._table_iterator(): if t in self.__binds: return self.__binds[t] if self.bind is not None: return self.bind elif isinstance(clause, sql.expression.ClauseElement) and clause.bind is not None: return clause.bind elif mapper is None: raise exceptions.UnboundExecutionError("Could not locate any mapper associated with SQL expression") else: if isinstance(mapper, type): mapper = _class_mapper(mapper) else: mapper = mapper.compile() e = mapper.mapped_table.bind if e is None: raise exceptions.UnboundExecutionError( "Could not locate any Engine or Connection bound to mapper '%s'" % str(mapper) ) return e
def get_bind(self, mapper, clause=None, **kwargs): """return an engine corresponding to the given arguments. mapper mapper relative to the desired operation clause a ClauseElement which is to be executed. if mapper is not present, this may be used to locate Table objects, which are then associated with mappers which have associated binds. \**kwargs Subclasses (i.e. ShardedSession) may add additional arguments to get_bind() which are passed through here. """ if mapper is None and clause is None: if self.bind is not None: return self.bind else: raise exceptions.InvalidRequestError( "This session is unbound to any Engine or Connection; specify a mapper to get_bind()" ) elif len(self.__binds): if mapper is not None: if isinstance(mapper, type): mapper = _class_mapper(mapper) if mapper.base_mapper in self.__binds: return self.__binds[mapper.base_mapper] elif mapper.compile().mapped_table in self.__binds: return self.__binds[mapper.mapped_table] if clause is not None: for t in clause._table_iterator(): if t in self.__binds: return self.__binds[t] if self.bind is not None: return self.bind else: if isinstance(mapper, type): mapper = _class_mapper(mapper) else: mapper = mapper.compile() e = mapper.mapped_table.bind if e is None: raise exceptions.InvalidRequestError( "Could not locate any Engine or Connection bound to mapper '%s'" % str(mapper)) return e
def connection(self, mapper_or_class, entity_name=None): if isinstance(mapper_or_class, type): mapper_or_class = _class_mapper(mapper_or_class, entity_name=entity_name) if self.parent is not None: return self.parent.connection(mapper_or_class) engine = self.session.get_bind(mapper_or_class) return self.get_or_add(engine)
def identity_key(self, *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)
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)
def query(self, mapper_or_class, entity_name=None, **kwargs): """return a new Query object corresponding to this Session and the mapper, or the classes' primary mapper.""" if isinstance(mapper_or_class, type): return query.Query( _class_mapper(mapper_or_class, entity_name=entity_name), self, **kwargs) else: return query.Query(mapper_or_class, self, **kwargs)
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.") key = mapper.identity_key_from_instance(instance) merged = None if key: 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: merged = attributes.new_instance(mapper.class_) self.save(merged, entity_name=mapper.entity_name) _recursive[instance] = merged for prop in mapper.iterate_properties: prop.merge(self, instance, merged, dont_load, _recursive) if dont_load: merged._state.commit_all() # remove any history return merged
def bind_mapper(self, mapper, bind, entity_name=None): """Bind the given `mapper` or `class` to the given ``Engine`` or ``Connection``. All subsequent operations involving this ``Mapper`` will use the given `bind`. """ if isinstance(mapper, type): mapper = _class_mapper(mapper, entity_name=entity_name) self.__binds[mapper.base_mapper] = bind for t in mapper._all_tables: self.__binds[t] = bind
def _save_impl(self, object, **kwargs): if hasattr(object, '_instance_key'): if not self.identity_map.has_key(object._instance_key): raise exceptions.InvalidRequestError("Instance '%s' is a detached instance or is already persistent in a different Session" % repr(object)) else: m = _class_mapper(object.__class__, entity_name=kwargs.get('entity_name', None)) # this would be a nice exception to raise...however this is incompatible with a contextual # session which puts all objects into the session upon construction. #if m._is_orphan(object): # raise exceptions.InvalidRequestError("Instance '%s' is an orphan, and must be attached to a parent object to be saved" % (repr(object))) m._assign_entity_name(object) self._register_pending(object)
def query(self, mapper_or_class, *addtl_entities, **kwargs): """Return a new ``Query`` object corresponding to this ``Session`` and the mapper, or the classes' primary mapper. """ entity_name = kwargs.pop('entity_name', None) if isinstance(mapper_or_class, type): q = self._query_cls(_class_mapper(mapper_or_class, entity_name=entity_name), self, **kwargs) else: q = self._query_cls(mapper_or_class, self, **kwargs) for ent in addtl_entities: q = q.add_entity(ent) return q
def _save_impl(self, object, **kwargs): if hasattr(object, '_instance_key'): if not self.identity_map.has_key(object._instance_key): raise exceptions.InvalidRequestError( "Instance '%s' is a detached instance or is already persistent in a different Session" % repr(object)) else: m = _class_mapper(object.__class__, entity_name=kwargs.get('entity_name', None)) # this would be a nice exception to raise...however this is incompatible with a contextual # session which puts all objects into the session upon construction. #if m._is_orphan(object): # raise exceptions.InvalidRequestError("Instance '%s' is an orphan, and must be attached to a parent object to be saved" % (repr(object))) m._assign_entity_name(object) self._register_pending(object)
def query(self, mapper_or_class, *addtl_entities, **kwargs): """Return a new ``Query`` object corresponding to this ``Session`` and the mapper, or the classes' primary mapper. """ entity_name = kwargs.pop('entity_name', None) if isinstance(mapper_or_class, type): q = self._query_cls( _class_mapper(mapper_or_class, entity_name=entity_name), self, **kwargs) else: q = self._query_cls(mapper_or_class, self, **kwargs) for ent in addtl_entities: q = q.add_entity(ent) return q
def merge(self, object, entity_name=None, _recursive=None): """Copy the state of the given `object` onto the persistent object 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 = util.Set() if entity_name is not None: mapper = _class_mapper(object.__class__, entity_name=entity_name) else: mapper = _object_mapper(object) if mapper in _recursive or object in _recursive: return None _recursive.add(mapper) _recursive.add(object) try: key = getattr(object, '_instance_key', None) if key is None: merged = attribute_manager.new_instance(mapper.class_) else: if key in self.identity_map: merged = self.identity_map[key] 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(object)) for prop in mapper.iterate_properties: prop.merge(self, object, merged, _recursive) if key is None: self.save(merged, entity_name=mapper.entity_name) return merged finally: _recursive.remove(mapper)
def merge(self, object, entity_name=None, _recursive=None): """Copy the state of the given `object` onto the persistent object 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 = util.Set() if entity_name is not None: mapper = _class_mapper(object.__class__, entity_name=entity_name) else: mapper = _object_mapper(object) if mapper in _recursive or object in _recursive: return None _recursive.add(mapper) _recursive.add(object) try: key = getattr(object, '_instance_key', None) if key is None: merged = mapper._create_instance(self) else: if key in self.identity_map: merged = self.identity_map[key] 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(object)) for prop in mapper.props.values(): prop.merge(self, object, merged, _recursive) if key is None: self.save(merged, entity_name=mapper.entity_name) return merged finally: _recursive.remove(mapper)
def query(self, mapper_or_class, entity_name=None, **kwargs): """return a new Query object corresponding to this Session and the mapper, or the classes' primary mapper.""" if isinstance(mapper_or_class, type): return query.Query(_class_mapper(mapper_or_class, entity_name=entity_name), self, **kwargs) else: return query.Query(mapper_or_class, self, **kwargs)
def mapper(self, class_, entity_name=None): """Given a ``Class``, return the primary ``Mapper`` responsible for persisting it.""" return _class_mapper(class_, entity_name = entity_name)
def mapper(self, class_, entity_name=None): """Given an Class, return the primary Mapper responsible for persisting it.""" return _class_mapper(class_, entity_name=entity_name)
def __init__(self, bind=None, autoflush=True, transactional=False, twophase=False, echo_uow=False, weak_identity_map=True, binds=None, extension=None): """Construct a new Session. autoflush when ``True``, all query operations will issue a ``flush()`` call to this ``Session`` before proceeding. This is a convenience feature so that ``flush()`` need not be called repeatedly in order for database queries to retrieve results. It's typical that ``autoflush`` is used in conjunction with ``transactional=True``, so that ``flush()`` is never called; you just call ``commit()`` when changes are complete to finalize all changes to the database. bind an optional ``Engine`` or ``Connection`` to which this ``Session`` should be bound. When specified, all SQL operations performed by this session will execute via this connectable. binds an optional dictionary, which contains more granular "bind" information than the ``bind`` parameter provides. This dictionary can map individual ``Table`` instances as well as ``Mapper`` instances to individual ``Engine`` or ``Connection`` objects. Operations which proceed relative to a particular ``Mapper`` will consult this dictionary for the direct ``Mapper`` instance as well as the mapper's ``mapped_table`` attribute in order to locate an connectable to use. The full resolution is described in the ``get_bind()`` method of ``Session``. Usage looks like:: sess = Session(binds={ SomeMappedClass : create_engine('postgres://engine1'), somemapper : create_engine('postgres://engine2'), some_table : create_engine('postgres://engine3'), }) Also see the ``bind_mapper()`` and ``bind_table()`` methods. echo_uow When ``True``, configure Python logging to dump all unit-of-work transactions. This is the equivalent of ``logging.getLogger('sqlalchemy.orm.unitofwork').setLevel(logging.DEBUG)``. extension an optional [sqlalchemy.orm.session#SessionExtension] instance, which will receive pre- and post- commit and flush events, as well as a post-rollback event. User- defined code may be placed within these hooks using a user-defined subclass of ``SessionExtension``. transactional Set up this ``Session`` to automatically begin transactions. Setting this flag to ``True`` is the rough equivalent of calling ``begin()`` after each ``commit()`` operation, after each ``rollback()``, and after each ``close()``. Basically, this has the effect that all session operations are performed within the context of a transaction. Note that the ``begin()`` operation does not immediately utilize any connection resources; only when connection resources are first required do they get allocated into a transactional context. twophase when ``True``, all transactions will be started using [sqlalchemy.engine_TwoPhaseTransaction]. During a ``commit()``, after ``flush()`` has been issued for all attached databaes, the ``prepare()`` method on each database's ``TwoPhaseTransaction`` will be called. This allows each database to roll back the entire transaction, before each transaction is committed. weak_identity_map When set to the default value of ``False``, a weak-referencing map is used; instances which are not externally referenced will be garbage collected immediately. For dereferenced instances which have pending changes present, the attribute management system will create a temporary strong-reference to the object which lasts until the changes are flushed to the database, at which point it's again dereferenced. Alternatively, when using the value ``True``, the identity map uses a regular Python dictionary to store instances. The session will maintain all instances present until they are removed using expunge(), clear(), or purge(). """ self.echo_uow = echo_uow self.weak_identity_map = weak_identity_map self.uow = unitofwork.UnitOfWork(self) self.identity_map = self.uow.identity_map self.bind = bind self.__binds = {} self.transaction = None self.hash_key = id(self) self.autoflush = autoflush self.transactional = transactional self.twophase = twophase self.extension = extension self._query_cls = query.Query self._mapper_flush_opts = {} if binds is not None: for mapperortable, value in binds.iteritems(): if isinstance(mapperortable, type): mapperortable = _class_mapper(mapperortable).base_mapper self.__binds[mapperortable] = value if isinstance(mapperortable, Mapper): for t in mapperortable._all_tables: self.__binds[t] = value if self.transactional: self.begin() _sessions[self.hash_key] = self
def __init__(self, bind=None, autoflush=True, transactional=False, twophase=False, echo_uow=False, weak_identity_map=True, binds=None, extension=None): """Construct a new Session. A session is usually constructed using the [sqlalchemy.orm#create_session()] function, or its more "automated" variant [sqlalchemy.orm#sessionmaker()]. autoflush When ``True``, all query operations will issue a ``flush()`` call to this ``Session`` before proceeding. This is a convenience feature so that ``flush()`` need not be called repeatedly in order for database queries to retrieve results. It's typical that ``autoflush`` is used in conjunction with ``transactional=True``, so that ``flush()`` is never called; you just call ``commit()`` when changes are complete to finalize all changes to the database. bind An optional ``Engine`` or ``Connection`` to which this ``Session`` should be bound. When specified, all SQL operations performed by this session will execute via this connectable. binds An optional dictionary, which contains more granular "bind" information than the ``bind`` parameter provides. This dictionary can map individual ``Table`` instances as well as ``Mapper`` instances to individual ``Engine`` or ``Connection`` objects. Operations which proceed relative to a particular ``Mapper`` will consult this dictionary for the direct ``Mapper`` instance as well as the mapper's ``mapped_table`` attribute in order to locate an connectable to use. The full resolution is described in the ``get_bind()`` method of ``Session``. Usage looks like:: sess = Session(binds={ SomeMappedClass : create_engine('postgres://engine1'), somemapper : create_engine('postgres://engine2'), some_table : create_engine('postgres://engine3'), }) Also see the ``bind_mapper()`` and ``bind_table()`` methods. echo_uow When ``True``, configure Python logging to dump all unit-of-work transactions. This is the equivalent of ``logging.getLogger('sqlalchemy.orm.unitofwork').setLevel(logging.DEBUG)``. extension An optional [sqlalchemy.orm.session#SessionExtension] instance, which will receive pre- and post- commit and flush events, as well as a post-rollback event. User- defined code may be placed within these hooks using a user-defined subclass of ``SessionExtension``. transactional Set up this ``Session`` to automatically begin transactions. Setting this flag to ``True`` is the rough equivalent of calling ``begin()`` after each ``commit()`` operation, after each ``rollback()``, and after each ``close()``. Basically, this has the effect that all session operations are performed within the context of a transaction. Note that the ``begin()`` operation does not immediately utilize any connection resources; only when connection resources are first required do they get allocated into a transactional context. twophase When ``True``, all transactions will be started using [sqlalchemy.engine_TwoPhaseTransaction]. During a ``commit()``, after ``flush()`` has been issued for all attached databases, the ``prepare()`` method on each database's ``TwoPhaseTransaction`` will be called. This allows each database to roll back the entire transaction, before each transaction is committed. weak_identity_map When set to the default value of ``False``, a weak-referencing map is used; instances which are not externally referenced will be garbage collected immediately. For dereferenced instances which have pending changes present, the attribute management system will create a temporary strong-reference to the object which lasts until the changes are flushed to the database, at which point it's again dereferenced. Alternatively, when using the value ``True``, the identity map uses a regular Python dictionary to store instances. The session will maintain all instances present until they are removed using expunge(), clear(), or purge(). """ self.echo_uow = echo_uow self.weak_identity_map = weak_identity_map self.uow = unitofwork.UnitOfWork(self) self.identity_map = self.uow.identity_map self.bind = bind self.__binds = {} self.transaction = None self.hash_key = id(self) self.autoflush = autoflush self.transactional = transactional self.twophase = twophase self.extension = extension self._query_cls = query.Query self._mapper_flush_opts = {} if binds is not None: for mapperortable, value in binds.iteritems(): if isinstance(mapperortable, type): mapperortable = _class_mapper(mapperortable).base_mapper self.__binds[mapperortable] = value if isinstance(mapperortable, Mapper): for t in mapperortable._all_tables: self.__binds[t] = value if self.transactional: self.begin() _sessions[self.hash_key] = self