/SQLAlchemy-0.7.8/lib/sqlalchemy/orm/attributes.py
Python | 1395 lines | 1365 code | 5 blank | 25 comment | 4 complexity | a14f326c0760e09e8b4843c6b73ad0aa MD5 | raw file
Large files files are truncated, but you can click here to view the full file
- # orm/attributes.py
- # Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
- #
- # This module is part of SQLAlchemy and is released under
- # the MIT License: http://www.opensource.org/licenses/mit-license.php
- """Defines instrumentation for class attributes and their interaction
- with instances.
- This module is usually not directly visible to user applications, but
- defines a large part of the ORM's interactivity.
- """
- import operator
- from operator import itemgetter
- from sqlalchemy import util, event, exc as sa_exc
- from sqlalchemy.orm import interfaces, collections, events, exc as orm_exc
- mapperutil = util.importlater("sqlalchemy.orm", "util")
- PASSIVE_NO_RESULT = util.symbol('PASSIVE_NO_RESULT')
- ATTR_WAS_SET = util.symbol('ATTR_WAS_SET')
- ATTR_EMPTY = util.symbol('ATTR_EMPTY')
- NO_VALUE = util.symbol('NO_VALUE')
- NEVER_SET = util.symbol('NEVER_SET')
- PASSIVE_RETURN_NEVER_SET = util.symbol('PASSIVE_RETURN_NEVER_SET',
- """Symbol indicating that loader callables can be
- fired off, but if no callable is applicable and no value is
- present, the attribute should remain non-initialized.
- NEVER_SET is returned in this case.
- """)
- PASSIVE_NO_INITIALIZE = util.symbol('PASSIVE_NO_INITIALIZE',
- """Symbol indicating that loader callables should
- not be fired off, and a non-initialized attribute
- should remain that way.
- """)
- PASSIVE_NO_FETCH = util.symbol('PASSIVE_NO_FETCH',
- """Symbol indicating that loader callables should not emit SQL,
- but a value can be fetched from the current session.
-
- Non-initialized attributes should be initialized to an empty value.
- """)
- PASSIVE_NO_FETCH_RELATED = util.symbol('PASSIVE_NO_FETCH_RELATED',
- """Symbol indicating that loader callables should not emit SQL for
- loading a related object, but can refresh the attributes of the local
- instance in order to locate a related object in the current session.
-
- Non-initialized attributes should be initialized to an empty value.
-
- The unit of work uses this mode to check if history is present
- on many-to-one attributes with minimal SQL emitted.
- """)
- PASSIVE_ONLY_PERSISTENT = util.symbol('PASSIVE_ONLY_PERSISTENT',
- """Symbol indicating that loader callables should only fire off for
- parent objects which are persistent (i.e., have a database
- identity).
- Load operations for the "previous" value of an attribute make
- use of this flag during change events.
- """)
- PASSIVE_OFF = util.symbol('PASSIVE_OFF',
- """Symbol indicating that loader callables should be executed
- normally.
- """)
- class QueryableAttribute(interfaces.PropComparator):
- """Base class for class-bound attributes. """
- def __init__(self, class_, key, impl=None,
- comparator=None, parententity=None):
- self.class_ = class_
- self.key = key
- self.impl = impl
- self.comparator = comparator
- self.parententity = parententity
- manager = manager_of_class(class_)
- # manager is None in the case of AliasedClass
- if manager:
- # propagate existing event listeners from
- # immediate superclass
- for base in manager._bases:
- if key in base:
- self.dispatch._update(base[key].dispatch)
- dispatch = event.dispatcher(events.AttributeEvents)
- dispatch.dispatch_cls._active_history = False
- @util.memoized_property
- def _supports_population(self):
- return self.impl.supports_population
- def get_history(self, instance, passive=PASSIVE_OFF):
- return self.impl.get_history(instance_state(instance),
- instance_dict(instance), passive)
- def __selectable__(self):
- # TODO: conditionally attach this method based on clause_element ?
- return self
- def __clause_element__(self):
- return self.comparator.__clause_element__()
- def label(self, name):
- return self.__clause_element__().label(name)
- def operate(self, op, *other, **kwargs):
- return op(self.comparator, *other, **kwargs)
- def reverse_operate(self, op, other, **kwargs):
- return op(other, self.comparator, **kwargs)
- def hasparent(self, state, optimistic=False):
- return self.impl.hasparent(state, optimistic=optimistic) is not False
- def __getattr__(self, key):
- try:
- return getattr(self.comparator, key)
- except AttributeError:
- raise AttributeError(
- 'Neither %r object nor %r object has an attribute %r' % (
- type(self).__name__,
- type(self.comparator).__name__,
- key)
- )
- def __str__(self):
- return "%s.%s" % (self.class_.__name__, self.key)
- @util.memoized_property
- def property(self):
- return self.comparator.property
- class InstrumentedAttribute(QueryableAttribute):
- """Class bound instrumented attribute which adds descriptor methods."""
- def __set__(self, instance, value):
- self.impl.set(instance_state(instance),
- instance_dict(instance), value, None)
- def __delete__(self, instance):
- self.impl.delete(instance_state(instance), instance_dict(instance))
- def __get__(self, instance, owner):
- if instance is None:
- return self
- dict_ = instance_dict(instance)
- if self._supports_population and self.key in dict_:
- return dict_[self.key]
- else:
- return self.impl.get(instance_state(instance),dict_)
- def create_proxied_attribute(descriptor):
- """Create an QueryableAttribute / user descriptor hybrid.
- Returns a new QueryableAttribute type that delegates descriptor
- behavior and getattr() to the given descriptor.
- """
- # TODO: can move this to descriptor_props if the need for this
- # function is removed from ext/hybrid.py
- class Proxy(QueryableAttribute):
- """Presents the :class:`.QueryableAttribute` interface as a
- proxy on top of a Python descriptor / :class:`.PropComparator`
- combination.
- """
- def __init__(self, class_, key, descriptor, comparator,
- adapter=None, doc=None):
- self.class_ = class_
- self.key = key
- self.descriptor = descriptor
- self._comparator = comparator
- self.adapter = adapter
- self.__doc__ = doc
- @property
- def property(self):
- return self.comparator.property
- @util.memoized_property
- def comparator(self):
- if util.callable(self._comparator):
- self._comparator = self._comparator()
- if self.adapter:
- self._comparator = self._comparator.adapted(self.adapter)
- return self._comparator
- def adapted(self, adapter):
- """Proxy adapted() for the use case of AliasedClass calling adapted."""
- return self.__class__(self.class_, self.key, self.descriptor,
- self._comparator,
- adapter)
- def __get__(self, instance, owner):
- if instance is None:
- return self
- else:
- return self.descriptor.__get__(instance, owner)
- def __str__(self):
- return self.key
- def __getattr__(self, attribute):
- """Delegate __getattr__ to the original descriptor and/or
- comparator."""
- try:
- return getattr(descriptor, attribute)
- except AttributeError:
- try:
- return getattr(self.comparator, attribute)
- except AttributeError:
- raise AttributeError(
- 'Neither %r object nor %r object has an attribute %r' % (
- type(descriptor).__name__,
- type(self.comparator).__name__,
- attribute)
- )
- Proxy.__name__ = type(descriptor).__name__ + 'Proxy'
- util.monkeypatch_proxied_specials(Proxy, type(descriptor),
- name='descriptor',
- from_instance=descriptor)
- return Proxy