"""
q = Session.query(Person).\
filter(Person.name.between("person %.2d" % start, "person %.2d" % end)).\
- options(*cache_address_bits).\
+ options(cache_address_bits).\
options(FromCache("default", "name_range"))
# have the "addresses" collection cached separately
from sqlalchemy.orm import eagerload
import os
-for p in Session.query(Person).options(eagerload(Person.addresses), *cache_address_bits):
+for p in Session.query(Person).options(eagerload(Person.addresses), cache_address_bits):
print p.format_full()
"""
if hasattr(self, 'cache_region'):
cache, cache_key = self._get_cache_plus_key()
-
ret = cache.get_value(cache_key, createfunc=lambda: list(Query.__iter__(self)))
return iter(self.session.merge(x, load=False) for x in ret)
else:
# Caching options. A set of three FromCache options
# which can be applied to Query(), causing the "lazy load"
# of these attributes to be loaded from cache.
-cache_address_bits = [
+cache_address_bits = (
FromCache("default", "byid", PostalCode.city),
FromCache("default", "byid", City.country),
FromCache("default", "byid", Address.postal_code),
- ]
+ )
return manager_of_class(instance.__class__).is_instrumented(key, search=True)
class InstrumentationRegistry(object):
- """Private instrumentation registration singleton."""
+ """Private instrumentation registration singleton.
+
+ All classes are routed through this registry
+ when first instrumented, however the InstrumentationRegistry
+ is not actually needed unless custom ClassManagers are in use.
+
+ """
_manager_finders = weakref.WeakKeyDictionary()
_state_finders = util.WeakIdentityMapping()
manager = _ClassInstrumentationAdapter(class_, manager)
if factory != ClassManager and not self._extended:
+ # somebody invoked a custom ClassManager.
+ # reinstall global "getter" functions with the more
+ # expensive ones.
self._extended = True
_install_lookup_strategy(self)
return factories
def manager_of_class(self, cls):
+ # this is only called when alternate instrumentation has been established
if cls is None:
return None
try:
del self._manager_finders[class_]
del self._state_finders[class_]
del self._dict_finders[class_]
-
+ if ClassManager.MANAGER_ATTR in class_.__dict__:
+ delattr(class_, ClassManager.MANAGER_ATTR)
+
instrumentation_registry = InstrumentationRegistry()
def _install_lookup_strategy(implementation):
and unit tests specific to this behavior.
"""
- global instance_state, instance_dict
+ global instance_state, instance_dict, manager_of_class
if implementation is util.symbol('native'):
instance_state = attrgetter(ClassManager.STATE_ATTR)
instance_dict = attrgetter("__dict__")
+ def manager_of_class(cls):
+ return cls.__dict__.get(ClassManager.MANAGER_ATTR, None)
else:
instance_state = instrumentation_registry.state_of
instance_dict = instrumentation_registry.dict_of
+ manager_of_class = instrumentation_registry.manager_of_class
-manager_of_class = instrumentation_registry.manager_of_class
_create_manager_for_cls = instrumentation_registry.create_manager_for_cls
+
+# Install default "lookup" strategies. These are basically
+# very fast attrgetters for key attributes.
+# When a custom ClassManager is installed, more expensive per-class
+# strategies are copied over these.
_install_lookup_strategy(util.symbol('native'))
def find_native_user_instrumentation_hook(cls):
"""
manager = attributes.manager_of_class(self.class_)
-
+
if self.non_primary:
if not manager or manager.mapper is None:
raise sa_exc.InvalidRequestError(
def get_property(self, key, resolve_synonyms=False, raiseerr=True):
"""return a MapperProperty associated with the given key."""
- self.compile()
+ if not self.compiled:
+ self.compile()
return self._get_property(key, resolve_synonyms=resolve_synonyms, raiseerr=raiseerr)
def _get_property(self, key, resolve_synonyms=False, raiseerr=True):
@property
def iterate_properties(self):
"""return an iterator of all MapperProperty objects."""
- self.compile()
+ if not self.compiled:
+ self.compile()
return self._props.itervalues()
def _mappers_from_spec(self, spec, selectable):
# most MapperOptions write to the '_attributes' dictionary,
# so copy that as well
self._attributes = self._attributes.copy()
- opts = [o for o in util.flatten_iterator(args)]
+ opts = list(util.flatten_iterator(args))
self._with_options = self._with_options + opts
if conditional:
for opt in opts:
util.warn_deprecated("dont_load=True has been renamed to load=False.")
_recursive = {}
- self._autoflush()
+
+ if load:
+ # flush current contents if we expect to load data
+ self._autoflush()
+
_object_mapper(instance) # verify mapped
autoflush = self.autoflush
try:
if self.modified:
self._strong_obj = state['instance']
- self.__dict__.update(
+ self.__dict__.update([
(k, state[k]) for k in (
'key', 'load_options', 'expired_attributes', 'mutable_dict'
) if k in state
- )
+ ])
if 'load_path' in state:
self.load_path = interfaces.deserialize_path(state['load_path'])
import sqlalchemy.exceptions as sa_exc
from sqlalchemy import sql, util
from sqlalchemy.sql import expression, util as sql_util, operators
-from sqlalchemy.orm.interfaces import MapperExtension, EXT_CONTINUE, PropComparator, MapperProperty, AttributeExtension
+from sqlalchemy.orm.interfaces import MapperExtension, EXT_CONTINUE, PropComparator, \
+ MapperProperty, AttributeExtension
from sqlalchemy.orm import attributes, exc
+mapperlib = None
all_cascades = frozenset(("delete", "delete-orphan", "all", "merge",
"expunge", "save-update", "refresh-expire",
"""
if isinstance(entity, AliasedClass):
return entity._AliasedClass__mapper, entity._AliasedClass__alias, True
- elif _is_mapped_class(entity):
- if isinstance(entity, type):
- mapper = class_mapper(entity, compile)
- else:
- if compile:
- mapper = entity.compile()
- else:
- mapper = entity
- return mapper, mapper._with_polymorphic_selectable, False
+
+ global mapperlib
+ if mapperlib is None:
+ from sqlalchemy.orm import mapperlib
+
+ if isinstance(entity, mapperlib.Mapper):
+ mapper = entity
+
+ elif isinstance(entity, type):
+ class_manager = attributes.manager_of_class(entity)
+
+ if class_manager is None:
+ return None, entity, False
+
+ mapper = class_manager.mapper
else:
return None, entity, False
+
+ if compile:
+ mapper = mapper.compile()
+ return mapper, mapper._with_polymorphic_selectable, False
def _entity_descriptor(entity, key):
"""Return attribute/property information given an entity and string name.
return bool(state.key)
def _is_mapped_class(cls):
- from sqlalchemy.orm import mapperlib as mapper
- if isinstance(cls, (AliasedClass, mapper.Mapper)):
+ global mapperlib
+ if mapperlib is None:
+ from sqlalchemy.orm import mapperlib
+ if isinstance(cls, (AliasedClass, mapperlib.Mapper)):
return True
if isinstance(cls, expression.ClauseElement):
return False
# down from 185 on this
# this is a small slice of a usually bigger
# operation so using a small variance
- @profiling.function_call_count(94, variance=0.001)
+ @profiling.function_call_count(87, variance=0.001)
def go():
return sess2.merge(p1, load=False)
# third call, merge object already present.
# almost no calls.
- @profiling.function_call_count(15, variance=0.001)
+ @profiling.function_call_count(10, variance=0.001)
def go():
return sess2.merge(p2, load=False)