summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/ext/declarative/base.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy/ext/declarative/base.py')
-rw-r--r--lib/sqlalchemy/ext/declarative/base.py95
1 files changed, 84 insertions, 11 deletions
diff --git a/lib/sqlalchemy/ext/declarative/base.py b/lib/sqlalchemy/ext/declarative/base.py
index 5a2b88db4..a764f126b 100644
--- a/lib/sqlalchemy/ext/declarative/base.py
+++ b/lib/sqlalchemy/ext/declarative/base.py
@@ -1,25 +1,27 @@
# ext/declarative/base.py
-# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
+# Copyright (C) 2005-2014 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
"""Internal implementation for declarative."""
from ...schema import Table, Column
-from ...orm import mapper, class_mapper
+from ...orm import mapper, class_mapper, synonym
from ...orm.interfaces import MapperProperty
from ...orm.properties import ColumnProperty, CompositeProperty
-from ...orm.util import _is_mapped_class
+from ...orm.attributes import QueryableAttribute
+from ...orm.base import _is_mapped_class
from ... import util, exc
from ...sql import expression
from ... import event
from . import clsregistry
-
+import collections
+import weakref
def _declared_mapping_info(cls):
# deferred mapping
- if cls in _MapperConfig.configs:
- return _MapperConfig.configs[cls]
+ if _DeferredMapperConfig.has_cls(cls):
+ return _DeferredMapperConfig.config_for_cls(cls)
# regular mapping
elif _is_mapped_class(cls):
return class_mapper(cls, configure=False)
@@ -148,6 +150,15 @@ def _as_declarative(cls, classname, dict_):
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 "
@@ -173,15 +184,19 @@ def _as_declarative(cls, classname, dict_):
# 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.
@@ -190,6 +205,15 @@ def _as_declarative(cls, classname, dict_):
# 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))))
+ )
+
declared_columns = sorted(
declared_columns, key=lambda c: c._creation_order)
table = None
@@ -281,19 +305,24 @@ def _as_declarative(cls, classname, dict_):
inherited_mapped_table is not inherited_table:
inherited_mapped_table._refresh_for_new_column(c)
- mt = _MapperConfig(mapper_cls,
+ 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 hasattr(cls, '_sa_decl_prepare'):
+ if not defer_map:
mt.map()
class _MapperConfig(object):
- configs = util.OrderedDict()
+
mapped_table = None
def __init__(self, mapper_cls,
@@ -311,7 +340,7 @@ class _MapperConfig(object):
self.mapper_args_fn = mapper_args_fn
self.declared_columns = declared_columns
self.column_copies = column_copies
- self.configs[cls] = self
+
def _prepare_mapper_arguments(self):
properties = self.properties
@@ -368,7 +397,6 @@ class _MapperConfig(object):
return result_mapper_args
def map(self):
- self.configs.pop(self.cls, None)
mapper_args = self._prepare_mapper_arguments()
self.cls.__mapper__ = self.mapper_cls(
self.cls,
@@ -376,6 +404,42 @@ class _MapperConfig(object):
**mapper_args
)
+class _DeferredMapperConfig(_MapperConfig):
+ _configs = util.OrderedDict()
+
+ @property
+ def cls(self):
+ return self._cls()
+
+ @cls.setter
+ def cls(self, class_):
+ self._cls = weakref.ref(class_, self._remove_config_cls)
+ self._configs[self._cls] = self
+
+ @classmethod
+ def _remove_config_cls(cls, ref):
+ cls._configs.pop(ref, None)
+
+ @classmethod
+ def has_cls(cls, class_):
+ # 2.6 fails on weakref if class_ is an old style class
+ return isinstance(class_, type) and \
+ weakref.ref(class_) in cls._configs
+
+ @classmethod
+ def config_for_cls(cls, class_):
+ return cls._configs[weakref.ref(class_)]
+
+
+ @classmethod
+ def classes_for_base(cls, base_cls):
+ return [m for m in cls._configs.values()
+ if issubclass(m.cls, base_cls)]
+
+ def map(self):
+ self._configs.pop(self._cls, None)
+ super(_DeferredMapperConfig, self).map()
+
def _add_attribute(cls, key, value):
"""add an attribute to an existing declarative class.
@@ -384,6 +448,7 @@ def _add_attribute(cls, key, value):
adds it to the Mapper, adds a column to the mapped Table, etc.
"""
+
if '__mapper__' in cls.__dict__:
if isinstance(value, Column):
_undefer_column_name(key, value)
@@ -400,6 +465,14 @@ def _add_attribute(cls, key, value):
key,
clsregistry._deferred_relationship(cls, value)
)
+ elif isinstance(value, QueryableAttribute) and value.key != key:
+ # detect a QueryableAttribute that's already mapped being
+ # assigned elsewhere in userland, turn into a synonym()
+ value = synonym(value.key)
+ cls.__mapper__.add_property(
+ key,
+ clsregistry._deferred_relationship(cls, value)
+ )
else:
type.__setattr__(cls, key, value)
else: