diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-08-02 14:22:25 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-08-02 14:22:25 -0400 |
commit | beaefd799dcc8954cbbbc9c8df9ede55cbee1e1c (patch) | |
tree | 7ecc71f8cb5dd01b3ec206bcdf31a647fa64c0e4 | |
parent | 32165f50209036a98959553e1c5e81537a091a15 (diff) | |
download | sqlalchemy-beaefd799dcc8954cbbbc9c8df9ede55cbee1e1c.tar.gz |
- poc fix for #3149
-rw-r--r-- | lib/sqlalchemy/ext/declarative/api.py | 41 | ||||
-rw-r--r-- | lib/sqlalchemy/ext/declarative/base.py | 25 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/schema.py | 6 |
3 files changed, 65 insertions, 7 deletions
diff --git a/lib/sqlalchemy/ext/declarative/api.py b/lib/sqlalchemy/ext/declarative/api.py index daf8bffb5..251b06fed 100644 --- a/lib/sqlalchemy/ext/declarative/api.py +++ b/lib/sqlalchemy/ext/declarative/api.py @@ -13,7 +13,7 @@ from ...orm import synonym as _orm_synonym, mapper,\ interfaces, properties from ...orm.util import polymorphic_union from ...orm.base import _mapper_or_none -from ...util import OrderedDict +from ...util import OrderedDict, classproperty from ... import exc import weakref @@ -164,6 +164,45 @@ class declared_attr(interfaces._MappedAttribute, property): def __get__(desc, self, cls): return desc.fget(cls) + @classproperty + def column(cls): + return _declared_column + + @classproperty + def property(cls): + return _declared_property + + defer_defer_defer = False + + +class _memoized_declared_attr(declared_attr): + def __init__(self, fget, cascading=False): + super(_memoized_declared_attr, self).__init__(fget) + self.reg = weakref.WeakKeyDictionary() + self._cascading = cascading + + def __get__(desc, self, cls): + if desc.defer_defer_defer: + return desc + elif cls in desc.reg: + return desc.reg[cls] + else: + desc.reg[cls] = obj = desc.fget(cls) + return obj + + @classproperty + def cascading(cls): + return lambda decorated: cls(decorated, cascading=True) + + +class _declared_column(_memoized_declared_attr): + pass + + +class _declared_property(_memoized_declared_attr): + defer_defer_defer = True + + def declarative_base(bind=None, metadata=None, mapper=None, cls=object, name='Base', constructor=_declarative_constructor, diff --git a/lib/sqlalchemy/ext/declarative/base.py b/lib/sqlalchemy/ext/declarative/base.py index 94baeeb51..cee22637d 100644 --- a/lib/sqlalchemy/ext/declarative/base.py +++ b/lib/sqlalchemy/ext/declarative/base.py @@ -33,7 +33,7 @@ def _declared_mapping_info(cls): def _as_declarative(cls, classname, dict_): - from .api import declared_attr + from .api import declared_attr, _memoized_declared_attr # dict_ will be a dictproxy, which we can't write to, and we need to! dict_ = dict(dict_) @@ -132,6 +132,14 @@ def _as_declarative(cls, classname, dict_): "column_property(), relationship(), etc.) must " "be declared as @declared_attr callables " "on declarative mixin classes.") + elif isinstance(obj, _memoized_declared_attr): # and \ + if obj._cascading: + dict_[name] = ret = obj.__get__(obj, cls) + else: + dict_[name] = ret = getattr(cls, name) + if isinstance(ret, (Column, MapperProperty)) and \ + ret.doc is None: + ret.doc = obj.__doc__ elif isinstance(obj, declarative_props): dict_[name] = ret = \ column_copies[obj] = getattr(cls, name) @@ -148,6 +156,7 @@ def _as_declarative(cls, classname, dict_): clsregistry.add_class(classname, cls) our_stuff = util.OrderedDict() + add_later = util.OrderedDict() for k in list(dict_): @@ -157,7 +166,10 @@ def _as_declarative(cls, classname, dict_): value = dict_[k] if isinstance(value, declarative_props): - value = getattr(cls, k) + if value.defer_defer_defer: + add_later[k] = value + else: + value = getattr(cls, k) elif isinstance(value, QueryableAttribute) and \ value.class_ is not cls and \ @@ -324,7 +336,8 @@ def _as_declarative(cls, classname, dict_): declared_columns, column_copies, our_stuff, - mapper_args_fn) + mapper_args_fn, + add_later) if not defer_map: mt.map() @@ -339,7 +352,8 @@ class _MapperConfig(object): inherits, declared_columns, column_copies, - properties, mapper_args_fn): + properties, mapper_args_fn, + add_later): self.mapper_cls = mapper_cls self.cls = cls self.local_table = table @@ -348,6 +362,7 @@ class _MapperConfig(object): self.mapper_args_fn = mapper_args_fn self.declared_columns = declared_columns self.column_copies = column_copies + self.add_later = add_later def _prepare_mapper_arguments(self): properties = self.properties @@ -410,6 +425,8 @@ class _MapperConfig(object): self.local_table, **mapper_args ) + for k, v in self.add_later.items(): + setattr(self.cls, k, v.fget(self.cls)) class _DeferredMapperConfig(_MapperConfig): diff --git a/lib/sqlalchemy/sql/schema.py b/lib/sqlalchemy/sql/schema.py index f3af46c40..ea8c32fd4 100644 --- a/lib/sqlalchemy/sql/schema.py +++ b/lib/sqlalchemy/sql/schema.py @@ -1161,8 +1161,10 @@ class Column(SchemaItem, ColumnClause): existing = getattr(self, 'table', None) if existing is not None and existing is not table: raise exc.ArgumentError( - "Column object already assigned to Table '%s'" % - existing.description) + "Column object '%s' already assigned to Table '%s'" % ( + self.key, + existing.description + )) if self.key in table._columns: col = table._columns.get(self.key) |