mirror of
https://github.com/sqlalchemy/sqlalchemy.git
synced 2026-05-31 12:58:45 -04:00
- @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]
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user