📄 mapper.py
字号:
# orm/mapper.py# Copyright (C) 2005, 2006, 2007, 2008 Michael Bayer mike_mp@zzzcomputing.com## This module is part of SQLAlchemy and is released under# the MIT License: http://www.opensource.org/licenses/mit-license.php"""Defines the [sqlalchemy.orm.mapper#Mapper] class, the central configurationalunit which associates a class with a database table.This is a semi-private module; the main configurational API of the ORM isavailable in [sqlalchemy.orm#]."""import weakreffrom itertools import chainfrom sqlalchemy import sql, util, exceptions, loggingfrom sqlalchemy.sql import expression, visitors, operators, util as sqlutilfrom sqlalchemy.sql.expression import _corresponding_column_or_errorfrom sqlalchemy.orm import sync, attributesfrom sqlalchemy.orm.util import ExtensionCarrier, create_row_adapter, state_str, instance_strfrom sqlalchemy.orm.interfaces import MapperProperty, EXT_CONTINUE, PropComparator__all__ = ['Mapper', 'class_mapper', 'object_mapper', '_mapper_registry']_mapper_registry = weakref.WeakKeyDictionary()# a list of MapperExtensions that will be installed in all mappers by defaultglobal_extensions = []# a constant returned by _get_attr_by_column to indicate# this mapper is not handling an attribute for a particular# columnNO_ATTRIBUTE = object()# lock used to synchronize the "mapper compile" step_COMPILE_MUTEX = util.threading.Lock()# initialize these two lazilyColumnProperty = NoneSynonymProperty = Noneclass Mapper(object): """Define the correlation of class attributes to database table columns. Instances of this class should be constructed via the [sqlalchemy.orm#mapper()] function. """ def __init__(self, class_, local_table, properties = None, primary_key = None, non_primary = False, inherits = None, inherit_condition = None, inherit_foreign_keys = None, extension = None, order_by = False, allow_column_override = False, entity_name = None, always_refresh = False, version_id_col = None, polymorphic_on=None, _polymorphic_map=None, polymorphic_identity=None, polymorphic_fetch=None, concrete=False, select_table=None, allow_null_pks=False, batch=True, column_prefix=None, include_properties=None, exclude_properties=None, eager_defaults=False): """Construct a new mapper. Mappers are normally constructed via the [sqlalchemy.orm#mapper()] function. See for details. """ if not issubclass(class_, object): raise exceptions.ArgumentError("Class '%s' is not a new-style class" % class_.__name__) for table in (local_table, select_table): if table is not None and isinstance(table, expression._SelectBaseMixin): # some db's, noteably postgres, dont want to select from a select # without an alias. also if we make our own alias internally, then # the configured properties on the mapper are not matched against the alias # we make, theres workarounds but it starts to get really crazy (its crazy enough # the SQL that gets generated) so just require an alias raise exceptions.ArgumentError("Mapping against a Select object requires that it has a name. Use an alias to give it a name, i.e. s = select(...).alias('myselect')") self.class_ = class_ self.entity_name = entity_name self.primary_key_argument = primary_key self.non_primary = non_primary self.order_by = order_by self.always_refresh = always_refresh self.version_id_col = version_id_col self.concrete = concrete self.single = False self.inherits = inherits self.select_table = select_table self.local_table = local_table self.inherit_condition = inherit_condition self.inherit_foreign_keys = inherit_foreign_keys self.extension = extension self._init_properties = properties or {} self.allow_column_override = allow_column_override self.allow_null_pks = allow_null_pks self.delete_orphans = [] self.batch = batch self.eager_defaults = eager_defaults self.column_prefix = column_prefix self.polymorphic_on = polymorphic_on self._eager_loaders = util.Set() self._row_translators = {} self._dependency_processors = [] self._clause_adapter = None # our 'polymorphic identity', a string name that when located in a result set row # indicates this Mapper should be used to construct the object instance for that row. self.polymorphic_identity = polymorphic_identity if polymorphic_fetch not in (None, 'union', 'select', 'deferred'): raise exceptions.ArgumentError("Invalid option for 'polymorphic_fetch': '%s'" % polymorphic_fetch) if polymorphic_fetch is None: self.polymorphic_fetch = (self.select_table is None) and 'select' or 'union' else: self.polymorphic_fetch = polymorphic_fetch # a dictionary of 'polymorphic identity' names, associating those names with # Mappers that will be used to construct object instances upon a select operation. if _polymorphic_map is None: self.polymorphic_map = {} else: self.polymorphic_map = _polymorphic_map self.columns = self.c = util.OrderedProperties() self.include_properties = include_properties self.exclude_properties = exclude_properties # a set of all mappers which inherit from this one. self._inheriting_mappers = util.Set() # a second mapper that is used for selecting, if the "select_table" argument # was sent to this mapper. self.__surrogate_mapper = None self.__props_init = False self.__should_log_info = logging.is_info_enabled(self.logger) self.__should_log_debug = logging.is_debug_enabled(self.logger) self._compile_class() self._compile_inheritance() self._compile_extensions() self._compile_tables() self._compile_properties() self._compile_pks() self._compile_selectable() self.__log("constructed") def __log(self, msg): if self.__should_log_info: self.logger.info("(" + self.class_.__name__ + "|" + (self.entity_name is not None and "/%s" % self.entity_name or "") + (self.local_table and self.local_table.description or str(self.local_table)) + (not self.non_primary and "|non-primary" or "") + ") " + msg) def __log_debug(self, msg): if self.__should_log_debug: self.logger.debug("(" + self.class_.__name__ + "|" + (self.entity_name is not None and "/%s" % self.entity_name or "") + (self.local_table and self.local_table.description or str(self.local_table)) + (not self.non_primary and "|non-primary" or "") + ") " + msg) def _is_orphan(self, obj): optimistic = has_identity(obj) for (key,klass) in self.delete_orphans: if attributes.has_parent(klass, obj, key, optimistic=optimistic): return False else: if self.delete_orphans: if not has_identity(obj): raise exceptions.FlushError("instance %s is an unsaved, pending instance and is an orphan (is not attached to %s)" % ( obj, ", nor ".join(["any parent '%s' instance via that classes' '%s' attribute" % (klass.__name__, key) for (key,klass) in self.delete_orphans]) )) else: return True else: return False def get_property(self, key, resolve_synonyms=False, raiseerr=True): """return a MapperProperty associated with the given key.""" self.compile() return self._get_property(key, resolve_synonyms=resolve_synonyms, raiseerr=raiseerr) def _get_property(self, key, resolve_synonyms=False, raiseerr=True): """private in-compilation version of get_property().""" prop = self.__props.get(key, None) if resolve_synonyms: while isinstance(prop, SynonymProperty): prop = self.__props.get(prop.name, None) if prop is None and raiseerr: raise exceptions.InvalidRequestError("Mapper '%s' has no property '%s'" % (str(self), key)) return prop def iterate_properties(self): self.compile() return self.__props.itervalues() iterate_properties = property(iterate_properties, doc="returns an iterator of all MapperProperty objects.") def properties(self): raise NotImplementedError("Public collection of MapperProperty objects is provided by the get_property() and iterate_properties accessors.") properties = property(properties) compiled = property(lambda self:self.__props_init, doc="return True if this mapper is compiled") def dispose(self): # disaable any attribute-based compilation self.__props_init = True try: del self.class_.c except AttributeError: pass if not self.non_primary and self.entity_name in self._class_state.mappers: del self._class_state.mappers[self.entity_name] if not self._class_state.mappers: attributes.unregister_class(self.class_) def compile(self): """Compile this mapper into its final internal format. """ if self.__props_init: return self _COMPILE_MUTEX.acquire() try: # double-check inside mutex if self.__props_init: return self # initialize properties on all mappers for mapper in list(_mapper_registry): if not mapper.__props_init: mapper.__initialize_properties() return self finally: _COMPILE_MUTEX.release() def __initialize_properties(self): """Call the ``init()`` method on all ``MapperProperties`` attached to this mapper. This happens after all mappers have completed compiling everything else up until this point, so that all dependencies are fully available. """ self.__log("_initialize_properties() started") l = [(key, prop) for key, prop in self.__props.iteritems()] for key, prop in l: self.__log("initialize prop " + key) if getattr(prop, 'key', None) is None: prop.init(key, self) self.__log("_initialize_properties() complete") self.__props_init = True def _compile_extensions(self): """Go through the global_extensions list as well as the list of ``MapperExtensions`` specified for this ``Mapper`` and creates a linked list of those extensions. """
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -