From: Mike Bayer Date: Fri, 26 Mar 2010 19:16:00 +0000 (-0400) Subject: - Using @classdecorator and similar on mixins to define X-Git-Tag: rel_0_6beta3~12^2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=15159a844d11b4a081bea4d7f6e3e3cf7dec937d;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - Using @classdecorator and similar on mixins to define __tablename__, __table_args__, etc. now works if the method references attributes on the ultimate subclass. [ticket:1749] --- diff --git a/CHANGES b/CHANGES index 7ad45a759c..fb0e67f9a2 100644 --- a/CHANGES +++ b/CHANGES @@ -92,6 +92,11 @@ CHANGES - Using a mixin won't break if the mixin implements an unpredictable __getattribute__(), i.e. Zope interfaces. [ticket:1746] + + - Using @classdecorator and similar on mixins to define + __tablename__, __table_args__, etc. now works if + the method references attributes on the ultimate + subclass. [ticket:1749] 0.6beta2 ======== diff --git a/lib/sqlalchemy/ext/declarative.py b/lib/sqlalchemy/ext/declarative.py index 435d381619..ef1d3e68c9 100644 --- a/lib/sqlalchemy/ext/declarative.py +++ b/lib/sqlalchemy/ext/declarative.py @@ -531,31 +531,32 @@ def instrument_declarative(cls, registry, metadata): def _as_declarative(cls, classname, dict_): - # 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_) column_copies = dict() - + unmapped_mixins = False for base in cls.__bases__: names = dir(base) if not _is_mapped_class(base): + unmapped_mixins = True for name in names: obj = getattr(base,name, None) if isinstance(obj, Column): dict_[name]=column_copies[obj]=obj.copy() - 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 + + # doing it this way enables these attributes to be descriptors + get_mapper_args = '__mapper_args__' in dict_ + get_table_args = '__table_args__' in dict_ + if unmapped_mixins: + get_mapper_args = get_mapper_args or getattr(cls,'__mapper_args__',None) + get_table_args = get_table_args or getattr(cls,'__table_args__',None) + tablename = getattr(cls,'__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 diff --git a/test/ext/test_declarative.py b/test/ext/test_declarative.py index fbe47ee7fc..bfc3ab074d 100644 --- a/test/ext/test_declarative.py +++ b/test/ext/test_declarative.py @@ -1958,6 +1958,18 @@ class DeclarativeMixinTest(DeclarativeTestBase): eq_(MyModel.__table__.name,'mymodel1') + def test_table_name_dependent_on_subclass(self): + class MyHistoryMixin: + @classproperty + def __tablename__(cls): + return cls.parent_name + '_changelog' + + class MyModel(Base, MyHistoryMixin): + parent_name = 'foo' + id = Column(Integer, primary_key=True) + + eq_(MyModel.__table__.name, 'foo_changelog') + def test_table_args_inherited(self): class MyMixin: