summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/ext/declarative/base.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2014-09-25 21:08:17 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2014-09-25 21:08:17 -0400
commit7f82c55fa764b031110309fb3a819e4b518e741d (patch)
treec65a20594f24697094d37d72ee3a34b062683c68 /lib/sqlalchemy/ext/declarative/base.py
parent5e7cb037e85acfbd8f064f7d4defb7ae07d0aff6 (diff)
downloadsqlalchemy-7f82c55fa764b031110309fb3a819e4b518e741d.tar.gz
- refactor of declarative, break up into indiviudal methods
that are now affixed to _MapperConfig - declarative now creates column copies ahead of time so that they are ready to go for a declared_attr - overhaul of declared_attr; memoization, cascading modifier - A relationship set up with :class:`.declared_attr` on a :class:`.AbstractConcreteBase` base class will now be configured on the abstract base mapping automatically, in addition to being set up on descendant concrete classes as usual. fixes #2670 - The :class:`.declared_attr` construct has newly improved behaviors and features in conjunction with declarative. The decorated function will now have access to the final column copies present on the local mixin when invoked, and will also be invoked exactly once for each mapped class, the returned result being memoized. A new modifier :attr:`.declared_attr.cascading` is added as well. fixes #3150 - the original plan for #3150 has been scaled back; by copying mixin columns up front and memoizing, we don't actually need the "map properties later" thing. - full docs + migration notes
Diffstat (limited to 'lib/sqlalchemy/ext/declarative/base.py')
-rw-r--r--lib/sqlalchemy/ext/declarative/base.py692
1 files changed, 393 insertions, 299 deletions
diff --git a/lib/sqlalchemy/ext/declarative/base.py b/lib/sqlalchemy/ext/declarative/base.py
index 94baeeb51..9cf07e208 100644
--- a/lib/sqlalchemy/ext/declarative/base.py
+++ b/lib/sqlalchemy/ext/declarative/base.py
@@ -19,6 +19,9 @@ from ... import event
from . import clsregistry
import collections
import weakref
+from sqlalchemy.orm import instrumentation
+
+declared_attr = declarative_props = None
def _declared_mapping_info(cls):
@@ -32,322 +35,402 @@ def _declared_mapping_info(cls):
return None
+def _get_immediate_cls_attr(cls, attrname):
+ """return an attribute of the class that is either present directly
+ on the class, e.g. not on a superclass, or is from a superclass but
+ this superclass is a mixin, that is, not a descendant of
+ the declarative base.
+
+ This is used to detect attributes that indicate something about
+ a mapped class independently from any mapped classes that it may
+ inherit from.
+
+ """
+ for base in cls.__mro__:
+ _is_declarative_inherits = hasattr(base, '_decl_class_registry')
+ if attrname in base.__dict__:
+ value = getattr(base, attrname)
+ if (base is cls or
+ (base in cls.__bases__ and not _is_declarative_inherits)):
+ return value
+ else:
+ return None
+
+
def _as_declarative(cls, classname, dict_):
- from .api import declared_attr
+ global declared_attr, declarative_props
+ if declared_attr is None:
+ from .api import declared_attr
+ declarative_props = (declared_attr, util.classproperty)
- # dict_ will be a dictproxy, which we can't write to, and we need to!
- dict_ = dict(dict_)
+ if _get_immediate_cls_attr(cls, '__abstract__'):
+ return
- column_copies = {}
- potential_columns = {}
+ _MapperConfig.setup_mapping(cls, classname, dict_)
- mapper_args_fn = None
- table_args = inherited_table_args = None
- tablename = None
- declarative_props = (declared_attr, util.classproperty)
+class _MapperConfig(object):
- for base in cls.__mro__:
- _is_declarative_inherits = hasattr(base, '_decl_class_registry')
+ @classmethod
+ def setup_mapping(cls, cls_, classname, dict_):
+ defer_map = _get_immediate_cls_attr(
+ cls_, '_sa_decl_prepare_nocascade') or \
+ hasattr(cls_, '_sa_decl_prepare')
- if '__declare_last__' in base.__dict__:
- @event.listens_for(mapper, "after_configured")
- def go():
- cls.__declare_last__()
- if '__declare_first__' in base.__dict__:
- @event.listens_for(mapper, "before_configured")
- def go():
- cls.__declare_first__()
- if '__abstract__' in base.__dict__ and base.__abstract__:
- if (base is cls or
- (base in cls.__bases__ and not _is_declarative_inherits)):
- return
+ if defer_map:
+ cfg_cls = _DeferredMapperConfig
+ else:
+ cfg_cls = _MapperConfig
+ cfg_cls(cls_, classname, dict_)
- class_mapped = _declared_mapping_info(base) is not None
+ def __init__(self, cls_, classname, dict_):
- for name, obj in vars(base).items():
- if name == '__mapper_args__':
- if not mapper_args_fn and (
- not class_mapped or
- isinstance(obj, declarative_props)
- ):
- # don't even invoke __mapper_args__ until
- # after we've determined everything about the
- # mapped table.
- # make a copy of it so a class-level dictionary
- # is not overwritten when we update column-based
- # arguments.
- mapper_args_fn = lambda: dict(cls.__mapper_args__)
- elif name == '__tablename__':
- if not tablename and (
- not class_mapped or
- isinstance(obj, declarative_props)
- ):
- tablename = cls.__tablename__
- elif name == '__table_args__':
- if not table_args and (
- not class_mapped or
- isinstance(obj, declarative_props)
- ):
- table_args = cls.__table_args__
- if not isinstance(table_args, (tuple, dict, type(None))):
- raise exc.ArgumentError(
- "__table_args__ value must be a tuple, "
- "dict, or None")
- if base is not cls:
- inherited_table_args = True
- elif class_mapped:
- if isinstance(obj, declarative_props):
- util.warn("Regular (i.e. not __special__) "
- "attribute '%s.%s' uses @declared_attr, "
- "but owning class %s is mapped - "
- "not applying to subclass %s."
- % (base.__name__, name, base, cls))
- continue
- elif base is not cls:
- # we're a mixin.
- if isinstance(obj, Column):
- if getattr(cls, name) is not obj:
- # if column has been overridden
- # (like by the InstrumentedAttribute of the
- # superclass), skip
+ self.cls = cls_
+
+ # dict_ will be a dictproxy, which we can't write to, and we need to!
+ self.dict_ = dict(dict_)
+ self.classname = classname
+ self.mapped_table = None
+ self.properties = util.OrderedDict()
+ self.declared_columns = set()
+ self.column_copies = {}
+ self._setup_declared_events()
+
+ # register up front, so that @declared_attr can memoize
+ # function evaluations in .info
+ manager = instrumentation.register_class(self.cls)
+ manager.info['declared_attr_reg'] = {}
+
+ self._scan_attributes()
+
+ clsregistry.add_class(self.classname, self.cls)
+
+ self._extract_mappable_attributes()
+
+ self._extract_declared_columns()
+
+ self._setup_table()
+
+ self._setup_inheritance()
+
+ self._early_mapping()
+
+ def _early_mapping(self):
+ self.map()
+
+ def _setup_declared_events(self):
+ if _get_immediate_cls_attr(self.cls, '__declare_last__'):
+ @event.listens_for(mapper, "after_configured")
+ def after_configured():
+ self.cls.__declare_last__()
+
+ if _get_immediate_cls_attr(self.cls, '__declare_first__'):
+ @event.listens_for(mapper, "before_configured")
+ def before_configured():
+ self.cls.__declare_first__()
+
+ def _scan_attributes(self):
+ cls = self.cls
+ dict_ = self.dict_
+ column_copies = self.column_copies
+ mapper_args_fn = None
+ table_args = inherited_table_args = None
+ tablename = None
+
+ for base in cls.__mro__:
+ class_mapped = base is not cls and \
+ _declared_mapping_info(base) is not None and \
+ not _get_immediate_cls_attr(base, '_sa_decl_prepare_nocascade')
+
+ if not class_mapped and base is not cls:
+ self._produce_column_copies(base)
+
+ for name, obj in vars(base).items():
+ if name == '__mapper_args__':
+ if not mapper_args_fn and (
+ not class_mapped or
+ isinstance(obj, declarative_props)
+ ):
+ # don't even invoke __mapper_args__ until
+ # after we've determined everything about the
+ # mapped table.
+ # make a copy of it so a class-level dictionary
+ # is not overwritten when we update column-based
+ # arguments.
+ mapper_args_fn = lambda: dict(cls.__mapper_args__)
+ elif name == '__tablename__':
+ if not tablename and (
+ not class_mapped or
+ isinstance(obj, declarative_props)
+ ):
+ tablename = cls.__tablename__
+ elif name == '__table_args__':
+ if not table_args and (
+ not class_mapped or
+ isinstance(obj, declarative_props)
+ ):
+ table_args = cls.__table_args__
+ if not isinstance(
+ table_args, (tuple, dict, type(None))):
+ raise exc.ArgumentError(
+ "__table_args__ value must be a tuple, "
+ "dict, or None")
+ if base is not cls:
+ inherited_table_args = True
+ elif class_mapped:
+ if isinstance(obj, declarative_props):
+ util.warn("Regular (i.e. not __special__) "
+ "attribute '%s.%s' uses @declared_attr, "
+ "but owning class %s is mapped - "
+ "not applying to subclass %s."
+ % (base.__name__, name, base, cls))
+ continue
+ elif base is not cls:
+ # we're a mixin, abstract base, or something that is
+ # acting like that for now.
+ if isinstance(obj, Column):
+ # already copied columns to the mapped class.
continue
- if obj.foreign_keys:
+ elif isinstance(obj, MapperProperty):
raise exc.InvalidRequestError(
- "Columns with foreign keys to other columns "
- "must be declared as @declared_attr callables "
- "on declarative mixin classes. ")
- if name not in dict_ and not (
- '__table__' in dict_ and
- (obj.name or name) in dict_['__table__'].c
- ) and name not in potential_columns:
- potential_columns[name] = \
- column_copies[obj] = \
- obj.copy()
- column_copies[obj]._creation_order = \
- obj._creation_order
- elif isinstance(obj, MapperProperty):
+ "Mapper properties (i.e. deferred,"
+ "column_property(), relationship(), etc.) must "
+ "be declared as @declared_attr callables "
+ "on declarative mixin classes.")
+ elif isinstance(obj, declarative_props):
+ oldclassprop = isinstance(obj, util.classproperty)
+ if not oldclassprop and obj._cascading:
+ dict_[name] = column_copies[obj] = \
+ ret = obj.__get__(obj, cls)
+ else:
+ if oldclassprop:
+ util.warn_deprecated(
+ "Use of sqlalchemy.util.classproperty on "
+ "declarative classes is deprecated.")
+ dict_[name] = column_copies[obj] = \
+ ret = getattr(cls, name)
+ if isinstance(ret, (Column, MapperProperty)) and \
+ ret.doc is None:
+ ret.doc = obj.__doc__
+
+ if inherited_table_args and not tablename:
+ table_args = None
+
+ self.table_args = table_args
+ self.tablename = tablename
+ self.mapper_args_fn = mapper_args_fn
+
+ def _produce_column_copies(self, base):
+ cls = self.cls
+ dict_ = self.dict_
+ column_copies = self.column_copies
+ # copy mixin columns to the mapped class
+ for name, obj in vars(base).items():
+ if isinstance(obj, Column):
+ if getattr(cls, name) is not obj:
+ # if column has been overridden
+ # (like by the InstrumentedAttribute of the
+ # superclass), skip
+ continue
+ elif obj.foreign_keys:
raise exc.InvalidRequestError(
- "Mapper properties (i.e. deferred,"
- "column_property(), relationship(), etc.) must "
- "be declared as @declared_attr callables "
- "on declarative mixin classes.")
- elif isinstance(obj, declarative_props):
- dict_[name] = ret = \
- column_copies[obj] = getattr(cls, name)
- if isinstance(ret, (Column, MapperProperty)) and \
- ret.doc is None:
- ret.doc = obj.__doc__
-
- # apply inherited columns as we should
- for k, v in potential_columns.items():
- dict_[k] = v
-
- if inherited_table_args and not tablename:
- table_args = None
-
- clsregistry.add_class(classname, cls)
- our_stuff = util.OrderedDict()
-
- for k in list(dict_):
-
- # TODO: improve this ? all dunders ?
- if k in ('__table__', '__tablename__', '__mapper_args__'):
- continue
-
- value = dict_[k]
- if isinstance(value, declarative_props):
- value = getattr(cls, k)
-
- elif isinstance(value, QueryableAttribute) and \
- value.class_ is not cls and \
- value.key != k:
- # detect a QueryableAttribute that's already mapped being
- # assigned elsewhere in userland, turn into a synonym()
- value = synonym(value.key)
- setattr(cls, k, value)
-
- if (isinstance(value, tuple) and len(value) == 1 and
- isinstance(value[0], (Column, MapperProperty))):
- util.warn("Ignoring declarative-like tuple value of attribute "
- "%s: possibly a copy-and-paste error with a comma "
- "left at the end of the line?" % k)
- continue
- if not isinstance(value, (Column, MapperProperty)):
- if not k.startswith('__'):
- dict_.pop(k)
- setattr(cls, k, value)
- continue
- if k == 'metadata':
- raise exc.InvalidRequestError(
- "Attribute name 'metadata' is reserved "
- "for the MetaData instance when using a "
- "declarative base class."
- )
- prop = clsregistry._deferred_relationship(cls, value)
- our_stuff[k] = prop
-
- # set up attributes in the order they were created
- our_stuff.sort(key=lambda key: our_stuff[key]._creation_order)
-
- # extract columns from the class dict
- declared_columns = set()
- name_to_prop_key = collections.defaultdict(set)
- for key, c in list(our_stuff.items()):
- if isinstance(c, (ColumnProperty, CompositeProperty)):
- for col in c.columns:
- if isinstance(col, Column) and \
- col.table is None:
- _undefer_column_name(key, col)
- if not isinstance(c, CompositeProperty):
- name_to_prop_key[col.name].add(key)
- declared_columns.add(col)
- elif isinstance(c, Column):
- _undefer_column_name(key, c)
- name_to_prop_key[c.name].add(key)
- declared_columns.add(c)
- # if the column is the same name as the key,
- # remove it from the explicit properties dict.
- # the normal rules for assigning column-based properties
- # will take over, including precedence of columns
- # in multi-column ColumnProperties.
- if key == c.key:
- del our_stuff[key]
-
- for name, keys in name_to_prop_key.items():
- if len(keys) > 1:
- util.warn(
- "On class %r, Column object %r named directly multiple times, "
- "only one will be used: %s" %
- (classname, name, (", ".join(sorted(keys))))
- )
+ "Columns with foreign keys to other columns "
+ "must be declared as @declared_attr callables "
+ "on declarative mixin classes. ")
+ elif name not in dict_ and not (
+ '__table__' in dict_ and
+ (obj.name or name) in dict_['__table__'].c
+ ):
+ column_copies[obj] = copy_ = obj.copy()
+ copy_._creation_order = obj._creation_order
+ setattr(cls, name, copy_)
+ dict_[name] = copy_
- declared_columns = sorted(
- declared_columns, key=lambda c: c._creation_order)
- table = None
+ def _extract_mappable_attributes(self):
+ cls = self.cls
+ dict_ = self.dict_
- if hasattr(cls, '__table_cls__'):
- table_cls = util.unbound_method_to_callable(cls.__table_cls__)
- else:
- table_cls = Table
-
- if '__table__' not in dict_:
- if tablename is not None:
-
- args, table_kw = (), {}
- if table_args:
- if isinstance(table_args, dict):
- table_kw = table_args
- elif isinstance(table_args, tuple):
- if isinstance(table_args[-1], dict):
- args, table_kw = table_args[0:-1], table_args[-1]
- else:
- args = table_args
-
- autoload = dict_.get('__autoload__')
- if autoload:
- table_kw['autoload'] = True
-
- cls.__table__ = table = table_cls(
- tablename, cls.metadata,
- *(tuple(declared_columns) + tuple(args)),
- **table_kw)
- else:
- table = cls.__table__
- if declared_columns:
- for c in declared_columns:
- if not table.c.contains_column(c):
- raise exc.ArgumentError(
- "Can't add additional column %r when "
- "specifying __table__" % c.key
- )
+ our_stuff = self.properties
- if hasattr(cls, '__mapper_cls__'):
- mapper_cls = util.unbound_method_to_callable(cls.__mapper_cls__)
- else:
- mapper_cls = mapper
+ for k in list(dict_):
- for c in cls.__bases__:
- if _declared_mapping_info(c) is not None:
- inherits = c
- break
- else:
- inherits = None
+ # TODO: improve this ? all dunders ?
+ if k in ('__table__', '__tablename__', '__mapper_args__'):
+ continue
- if table is None and inherits is None:
- raise exc.InvalidRequestError(
- "Class %r does not have a __table__ or __tablename__ "
- "specified and does not inherit from an existing "
- "table-mapped class." % cls
- )
- elif inherits:
- inherited_mapper = _declared_mapping_info(inherits)
- inherited_table = inherited_mapper.local_table
- inherited_mapped_table = inherited_mapper.mapped_table
-
- if table is None:
- # single table inheritance.
- # ensure no table args
- if table_args:
- raise exc.ArgumentError(
- "Can't place __table_args__ on an inherited class "
- "with no table."
+ value = dict_[k]
+ if isinstance(value, declarative_props):
+ value = getattr(cls, k)
+
+ elif isinstance(value, QueryableAttribute) and \
+ value.class_ is not cls and \
+ value.key != k:
+ # detect a QueryableAttribute that's already mapped being
+ # assigned elsewhere in userland, turn into a synonym()
+ value = synonym(value.key)
+ setattr(cls, k, value)
+
+ if (isinstance(value, tuple) and len(value) == 1 and
+ isinstance(value[0], (Column, MapperProperty))):
+ util.warn("Ignoring declarative-like tuple value of attribute "
+ "%s: possibly a copy-and-paste error with a comma "
+ "left at the end of the line?" % k)
+ continue
+ if not isinstance(value, (Column, MapperProperty)):
+ if not k.startswith('__'):
+ dict_.pop(k)
+ setattr(cls, k, value)
+ continue
+ if k == 'metadata':
+ raise exc.InvalidRequestError(
+ "Attribute name 'metadata' is reserved "
+ "for the MetaData instance when using a "
+ "declarative base class."
+ )
+ prop = clsregistry._deferred_relationship(cls, value)
+ our_stuff[k] = prop
+
+ def _extract_declared_columns(self):
+ our_stuff = self.properties
+
+ # set up attributes in the order they were created
+ our_stuff.sort(key=lambda key: our_stuff[key]._creation_order)
+
+ # extract columns from the class dict
+ declared_columns = self.declared_columns
+ name_to_prop_key = collections.defaultdict(set)
+ for key, c in list(our_stuff.items()):
+ if isinstance(c, (ColumnProperty, CompositeProperty)):
+ for col in c.columns:
+ if isinstance(col, Column) and \
+ col.table is None:
+ _undefer_column_name(key, col)
+ if not isinstance(c, CompositeProperty):
+ name_to_prop_key[col.name].add(key)
+ declared_columns.add(col)
+ elif isinstance(c, Column):
+ _undefer_column_name(key, c)
+ name_to_prop_key[c.name].add(key)
+ declared_columns.add(c)
+ # if the column is the same name as the key,
+ # remove it from the explicit properties dict.
+ # the normal rules for assigning column-based properties
+ # will take over, including precedence of columns
+ # in multi-column ColumnProperties.
+ if key == c.key:
+ del our_stuff[key]
+
+ for name, keys in name_to_prop_key.items():
+ if len(keys) > 1:
+ util.warn(
+ "On class %r, Column object %r named "
+ "directly multiple times, "
+ "only one will be used: %s" %
+ (self.classname, name, (", ".join(sorted(keys))))
)
- # add any columns declared here to the inherited table.
- for c in declared_columns:
- if c.primary_key:
- raise exc.ArgumentError(
- "Can't place primary key columns on an inherited "
- "class with no table."
- )
- if c.name in inherited_table.c:
- if inherited_table.c[c.name] is c:
- continue
- raise exc.ArgumentError(
- "Column '%s' on class %s conflicts with "
- "existing column '%s'" %
- (c, cls, inherited_table.c[c.name])
- )
- inherited_table.append_column(c)
- if inherited_mapped_table is not None and \
- inherited_mapped_table is not inherited_table:
- inherited_mapped_table._refresh_for_new_column(c)
-
- defer_map = hasattr(cls, '_sa_decl_prepare')
- if defer_map:
- cfg_cls = _DeferredMapperConfig
- else:
- cfg_cls = _MapperConfig
- mt = cfg_cls(mapper_cls,
- cls, table,
- inherits,
- declared_columns,
- column_copies,
- our_stuff,
- mapper_args_fn)
- if not defer_map:
- mt.map()
+ def _setup_table(self):
+ cls = self.cls
+ tablename = self.tablename
+ table_args = self.table_args
+ dict_ = self.dict_
+ declared_columns = self.declared_columns
-class _MapperConfig(object):
+ declared_columns = self.declared_columns = sorted(
+ declared_columns, key=lambda c: c._creation_order)
+ table = None
- mapped_table = None
-
- def __init__(self, mapper_cls,
- cls,
- table,
- inherits,
- declared_columns,
- column_copies,
- properties, mapper_args_fn):
- self.mapper_cls = mapper_cls
- self.cls = cls
+ if hasattr(cls, '__table_cls__'):
+ table_cls = util.unbound_method_to_callable(cls.__table_cls__)
+ else:
+ table_cls = Table
+
+ if '__table__' not in dict_:
+ if tablename is not None:
+
+ args, table_kw = (), {}
+ if table_args:
+ if isinstance(table_args, dict):
+ table_kw = table_args
+ elif isinstance(table_args, tuple):
+ if isinstance(table_args[-1], dict):
+ args, table_kw = table_args[0:-1], table_args[-1]
+ else:
+ args = table_args
+
+ autoload = dict_.get('__autoload__')
+ if autoload:
+ table_kw['autoload'] = True
+
+ cls.__table__ = table = table_cls(
+ tablename, cls.metadata,
+ *(tuple(declared_columns) + tuple(args)),
+ **table_kw)
+ else:
+ table = cls.__table__
+ if declared_columns:
+ for c in declared_columns:
+ if not table.c.contains_column(c):
+ raise exc.ArgumentError(
+ "Can't add additional column %r when "
+ "specifying __table__" % c.key
+ )
self.local_table = table
- self.inherits = inherits
- self.properties = properties
- self.mapper_args_fn = mapper_args_fn
- self.declared_columns = declared_columns
- self.column_copies = column_copies
+
+ def _setup_inheritance(self):
+ table = self.local_table
+ cls = self.cls
+ table_args = self.table_args
+ declared_columns = self.declared_columns
+ for c in cls.__bases__:
+ if _declared_mapping_info(c) is not None and \
+ not _get_immediate_cls_attr(
+ c, '_sa_decl_prepare_nocascade'):
+ self.inherits = c
+ break
+ else:
+ self.inherits = None
+
+ if table is None and self.inherits is None and \
+ not _get_immediate_cls_attr(cls, '__no_table__'):
+
+ raise exc.InvalidRequestError(
+ "Class %r does not have a __table__ or __tablename__ "
+ "specified and does not inherit from an existing "
+ "table-mapped class." % cls
+ )
+ elif self.inherits:
+ inherited_mapper = _declared_mapping_info(self.inherits)
+ inherited_table = inherited_mapper.local_table
+ inherited_mapped_table = inherited_mapper.mapped_table
+
+ if table is None:
+ # single table inheritance.
+ # ensure no table args
+ if table_args:
+ raise exc.ArgumentError(
+ "Can't place __table_args__ on an inherited class "
+ "with no table."
+ )
+ # add any columns declared here to the inherited table.
+ for c in declared_columns:
+ if c.primary_key:
+ raise exc.ArgumentError(
+ "Can't place primary key columns on an inherited "
+ "class with no table."
+ )
+ if c.name in inherited_table.c:
+ if inherited_table.c[c.name] is c:
+ continue
+ raise exc.ArgumentError(
+ "Column '%s' on class %s conflicts with "
+ "existing column '%s'" %
+ (c, cls, inherited_table.c[c.name])
+ )
+ inherited_table.append_column(c)
+ if inherited_mapped_table is not None and \
+ inherited_mapped_table is not inherited_table:
+ inherited_mapped_table._refresh_for_new_column(c)
def _prepare_mapper_arguments(self):
properties = self.properties
@@ -401,20 +484,31 @@ class _MapperConfig(object):
properties[k] = [col] + p.columns
result_mapper_args = mapper_args.copy()
result_mapper_args['properties'] = properties
- return result_mapper_args
+ self.mapper_args = result_mapper_args
def map(self):
- mapper_args = self._prepare_mapper_arguments()
- self.cls.__mapper__ = self.mapper_cls(
+ self._prepare_mapper_arguments()
+ if hasattr(self.cls, '__mapper_cls__'):
+ mapper_cls = util.unbound_method_to_callable(
+ self.cls.__mapper_cls__)
+ else:
+ mapper_cls = mapper
+
+ self.cls.__mapper__ = mp_ = mapper_cls(
self.cls,
self.local_table,
- **mapper_args
+ **self.mapper_args
)
+ del mp_.class_manager.info['declared_attr_reg']
+ return mp_
class _DeferredMapperConfig(_MapperConfig):
_configs = util.OrderedDict()
+ def _early_mapping(self):
+ pass
+
@property
def cls(self):
return self._cls()
@@ -466,7 +560,7 @@ class _DeferredMapperConfig(_MapperConfig):
def map(self):
self._configs.pop(self._cls, None)
- super(_DeferredMapperConfig, self).map()
+ return super(_DeferredMapperConfig, self).map()
def _add_attribute(cls, key, value):