From a1337a822a889323f4780b510e4b4992d220e797 Mon Sep 17 00:00:00 2001 From: Chris Withers Date: Tue, 2 Mar 2010 10:17:31 +0000 Subject: [PATCH] fixes for some nasty edge cases when usng descriptors to compute special attributes --- lib/sqlalchemy/ext/declarative.py | 21 ++++++++++++++++----- test/ext/test_declarative.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/lib/sqlalchemy/ext/declarative.py b/lib/sqlalchemy/ext/declarative.py index 12b79c7965..fe45e6c175 100644 --- a/lib/sqlalchemy/ext/declarative.py +++ b/lib/sqlalchemy/ext/declarative.py @@ -528,9 +528,10 @@ def instrument_declarative(cls, registry, metadata): def _as_declarative(cls, classname, dict_): - # this spelling enables these attributes to be descriptors - mapper_args = '__mapper_args__' in dict_ and cls.__mapper_args__ or {} - table_args = '__table_args__' in dict_ and cls.__table_args__ or None + # doing it this way enables these attributes to be descriptors, + # see below... + get_mapper_args = '__mapper_args__' in dict_ + get_table_args = '__table_args__' in dict_ # dict_ will be a dictproxy, which we can't write to, and we need to! dict_ = dict(dict_) @@ -544,12 +545,20 @@ def _as_declarative(cls, classname, dict_): obj = getattr(base,name) if isinstance(obj, Column): dict_[name]=column_copies[obj]=obj.copy() - mapper_args = mapper_args or getattr(base,'__mapper_args__',mapper_args) - table_args = table_args or getattr(base,'__table_args__',None) + get_mapper_args = get_mapper_args or getattr(base,'__mapper_args__',None) + get_table_args = get_table_args or getattr(base,'__table_args__',None) tablename = getattr(base,'__tablename__',None) if tablename: + # subtle: if tablename is a descriptor here, we actually + # put the wrong value in, but it serves as a marker to get + # the right value value... dict_['__tablename__']=tablename + # now that we know whether or not to get these, get them from the class + # if we should, enabling them to be decorators + mapper_args = get_mapper_args and cls.__mapper_args__ or {} + table_args = get_table_args and cls.__table_args__ or None + # make sure that column copies are used rather than the original columns # from any mixins for k, v in mapper_args.iteritems(): @@ -595,6 +604,8 @@ def _as_declarative(cls, classname, dict_): table = None if '__table__' not in dict_: if '__tablename__' in dict_: + # see above: if __tablename__ is a descriptor, this + # means we get the right value used! tablename = cls.__tablename__ if isinstance(table_args, dict): diff --git a/test/ext/test_declarative.py b/test/ext/test_declarative.py index a22a9581ea..3d11c0b4dc 100644 --- a/test/ext/test_declarative.py +++ b/test/ext/test_declarative.py @@ -1900,6 +1900,19 @@ class DeclarativeMixinTest(DeclarativeTestBase): eq_(MyModel.__table__.kwargs,{'mysql_engine': 'InnoDB'}) + def test_table_args_inherited_descriptor(self): + + class MyMixin: + @classproperty + def __table_args__(cls): + return {'info':cls.__name__} + + class MyModel(Base,MyMixin): + __tablename__='test' + id = Column(Integer, primary_key=True) + + eq_(MyModel.__table__.info,'MyModel') + def test_table_args_inherited_single_table_inheritance(self): class MyMixin: @@ -1967,6 +1980,23 @@ class DeclarativeMixinTest(DeclarativeTestBase): eq_(MyModel.__mapper__.always_refresh,True) + def test_mapper_args_inherited_descriptor(self): + + class MyMixin: + @classproperty + def __mapper_args__(cls): + # tenuous, but illustrates the problem! + if cls.__name__=='MyModel': + return dict(always_refresh=True) + else: + return dict(always_refresh=False) + + class MyModel(Base,MyMixin): + __tablename__='test' + id = Column(Integer, primary_key=True) + + eq_(MyModel.__mapper__.always_refresh,True) + def test_mapper_args_polymorphic_on_inherited(self): class MyMixin: -- 2.47.3