From: Mike Bayer Date: Mon, 20 Sep 2010 16:00:14 +0000 (-0400) Subject: - @classproperty (soon/now @mapperproperty) takes effect for X-Git-Tag: rel_0_6_5~56 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4fbd16e045cc0daed754f2c4f9732ba1ac128205;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - @classproperty (soon/now @mapperproperty) takes effect for __mapper_args__, __table_args__, __tablename__ on a base class that is not a mixin, as well as mixins. [ticket:1922] --- diff --git a/CHANGES b/CHANGES index 7d72f8769f..04457ef447 100644 --- a/CHANGES +++ b/CHANGES @@ -103,6 +103,12 @@ CHANGES - as_scalar(), label() can be called on a selectable which contains a Column that is not yet named. [ticket:1862] + +- declarative + - @classproperty (soon/now @mapperproperty) takes effect for + __mapper_args__, __table_args__, __tablename__ on + a base class that is not a mixin, as well as mixins. + [ticket:1922] - engine diff --git a/lib/sqlalchemy/ext/declarative.py b/lib/sqlalchemy/ext/declarative.py index 40263c7b80..ad08d7873d 100755 --- a/lib/sqlalchemy/ext/declarative.py +++ b/lib/sqlalchemy/ext/declarative.py @@ -908,51 +908,63 @@ def _as_declarative(cls, classname, dict_): parent_columns = () for base in cls.__mro__: - if _is_mapped_class(base): + class_mapped = _is_mapped_class(base) + if class_mapped: parent_columns = base.__table__.c.keys() - else: - for name,obj in vars(base).items(): - if name == '__mapper_args__': - if not mapper_args: - mapper_args = cls.__mapper_args__ - elif name == '__tablename__': - if not tablename: - tablename = cls.__tablename__ - elif name == '__table_args__': - if not table_args: - table_args = cls.__table_args__ - if base is not cls: - inherited_table_args = True - elif base is not cls: - # we're a mixin. - - if isinstance(obj, Column): - if obj.foreign_keys: - raise exceptions.InvalidRequestError( - "Columns with foreign keys to other columns " - "must be declared as @classproperty callables " - "on declarative mixin classes. ") - if name not in dict_ and not ( - '__table__' in dict_ and - name in dict_['__table__'].c - ): - potential_columns[name] = \ - column_copies[obj] = \ - obj.copy() - column_copies[obj]._creation_order = \ - obj._creation_order - elif isinstance(obj, MapperProperty): + + for name,obj in vars(base).items(): + if name == '__mapper_args__': + if not mapper_args and ( + not class_mapped or + isinstance(obj, util.classproperty) + ): + mapper_args = cls.__mapper_args__ + elif name == '__tablename__': + if not tablename and ( + not class_mapped or + isinstance(obj, util.classproperty) + ): + tablename = cls.__tablename__ + elif name == '__table_args__': + if not table_args and ( + not class_mapped or + isinstance(obj, util.classproperty) + ): + table_args = cls.__table_args__ + if base is not cls: + inherited_table_args = True + elif class_mapped: + continue + elif base is not cls: + # we're a mixin. + + if isinstance(obj, Column): + if obj.foreign_keys: raise exceptions.InvalidRequestError( - "Mapper properties (i.e. deferred," - "column_property(), relationship(), etc.) must " - "be declared as @classproperty callables " - "on declarative mixin classes.") - elif isinstance(obj, util.classproperty): - dict_[name] = ret = \ - column_copies[obj] = getattr(cls, name) - if isinstance(ret, (Column, MapperProperty)) and \ - ret.doc is None: - ret.doc = obj.__doc__ + "Columns with foreign keys to other columns " + "must be declared as @classproperty callables " + "on declarative mixin classes. ") + if name not in dict_ and not ( + '__table__' in dict_ and + name in dict_['__table__'].c + ): + potential_columns[name] = \ + column_copies[obj] = \ + obj.copy() + column_copies[obj]._creation_order = \ + obj._creation_order + elif isinstance(obj, MapperProperty): + raise exceptions.InvalidRequestError( + "Mapper properties (i.e. deferred," + "column_property(), relationship(), etc.) must " + "be declared as @classproperty callables " + "on declarative mixin classes.") + elif isinstance(obj, util.classproperty): + 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(): diff --git a/test/ext/test_declarative.py b/test/ext/test_declarative.py index 65ec92ca29..f628d1dc74 100644 --- a/test/ext/test_declarative.py +++ b/test/ext/test_declarative.py @@ -2458,18 +2458,62 @@ class DeclarativeMixinTest(DeclarativeTestBase): __tablename__ = 'test' @classproperty - def __mapper_args__(self): + def __mapper_args__(cls): args = {} args.update(MyMixin1.__mapper_args__) args.update(MyMixin2.__mapper_args__) + if cls.__name__ != 'MyModel': + args.pop('polymorphic_on') + args['polymorphic_identity'] = cls.__name__ + return args id = Column(Integer, primary_key=True) - - col = MyModel.__mapper__.polymorphic_on - eq_(col.name, 'type_') - assert col.table is not None + + class MySubModel(MyModel): + pass + + eq_( + MyModel.__mapper__.polymorphic_on.name, + 'type_' + ) + assert MyModel.__mapper__.polymorphic_on.table is not None eq_(MyModel.__mapper__.always_refresh, True) + eq_(MySubModel.__mapper__.always_refresh, True) + eq_(MySubModel.__mapper__.polymorphic_identity, 'MySubModel') + + def test_mapper_args_property(self): + class MyModel(Base): + + @classproperty + def __tablename__(cls): + return cls.__name__.lower() + + @classproperty + def __table_args__(cls): + return {'mysql_engine':'InnoDB'} + + @classproperty + def __mapper_args__(cls): + args = {} + args['polymorphic_identity'] = cls.__name__ + return args + id = Column(Integer, primary_key=True) + + class MySubModel(MyModel): + id = Column(Integer, ForeignKey('mymodel.id'), primary_key=True) + class MySubModel2(MyModel): + __tablename__ = 'sometable' + id = Column(Integer, ForeignKey('mymodel.id'), primary_key=True) + + eq_(MyModel.__mapper__.polymorphic_identity, 'MyModel') + eq_(MySubModel.__mapper__.polymorphic_identity, 'MySubModel') + eq_(MyModel.__table__.kwargs['mysql_engine'], 'InnoDB') + eq_(MySubModel.__table__.kwargs['mysql_engine'], 'InnoDB') + eq_(MySubModel2.__table__.kwargs['mysql_engine'], 'InnoDB') + eq_(MyModel.__table__.name, 'mymodel') + eq_(MySubModel.__table__.name, 'mysubmodel') + def test_single_table_no_propagation(self): class IdColumn: