which can't be properly recreated at this level. For columns that
have foreign keys, as well as for the variety of mapper-level constructs
that require destination-explicit context, the
-:func:`~.mapperproperty` decorator is provided so that
+:func:`~.declared_attr` decorator is provided so that
patterns common to many classes can be defined as callables::
- from sqlalchemy.ext.declarative import mapperproperty
+ from sqlalchemy.ext.declarative import declared_attr
class ReferenceAddressMixin(object):
- @mapperproperty
+ @declared_attr
def address_id(cls):
return Column(Integer, ForeignKey('address.id'))
extension can use the resulting :class:`Column` object as returned by
the method without the need to copy it.
-Columns generated by :func:`~.mapperproperty` can also be
+Columns generated by :func:`~.declared_attr` can also be
referenced by ``__mapper_args__`` to a limited degree, currently
by ``polymorphic_on`` and ``version_id_col``, by specifying the
classdecorator itself into the dictionary - the declarative extension
will resolve them at class construction time::
class MyMixin:
- @mapperproperty
+ @declared_attr
def type_(cls):
return Column(String(50))
Relationships created by :func:`~sqlalchemy.orm.relationship` are provided
with declarative mixin classes exclusively using the
-:func:`.mapperproperty` approach, eliminating any ambiguity
+:func:`.declared_attr` approach, eliminating any ambiguity
which could arise when copying a relationship and its possibly column-bound
contents. Below is an example which combines a foreign key column and a
relationship so that two classes ``Foo`` and ``Bar`` can both be configured to
reference a common target class via many-to-one::
class RefTargetMixin(object):
- @mapperproperty
+ @declared_attr
def target_id(cls):
return Column('target_id', ForeignKey('target.id'))
- @mapperproperty
+ @declared_attr
def target(cls):
return relationship("Target")
to get it's name::
class RefTargetMixin(object):
- @mapperproperty
+ @declared_attr
def target_id(cls):
return Column('target_id', ForeignKey('target.id'))
- @mapperproperty
+ @declared_attr
def target(cls):
return relationship("Target",
primaryjoin="Target.id==%s.target_id" % cls.__name__
:class:`~sqlalchemy.orm.interfaces.MapperProperty` subclasses such as
:func:`~sqlalchemy.orm.deferred`, :func:`~sqlalchemy.orm.column_property`,
etc. ultimately involve references to columns, and therefore, when
-used with declarative mixins, have the :func:`.mapperproperty`
+used with declarative mixins, have the :func:`.declared_attr`
requirement so that no reliance on copying is needed::
class SomethingMixin(object):
- @mapperproperty
+ @declared_attr
def dprop(cls):
return deferred(Column(Integer))
hierarchy, you can explicitly specify ``__tablename__`` as ``None`` to
indicate that the class should not have a table mapped::
- from sqlalchemy.ext.declarative import mapperproperty
+ from sqlalchemy.ext.declarative import declared_attr
class Tablename:
- @mapperproperty
+ @declared_attr
def __tablename__(cls):
return cls.__name__.lower()
As an example, here's a mixin that will only allow single table
inheritance::
- from sqlalchemy.ext.declarative import mapperproperty
+ from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.ext.declarative import has_inherited_table
class Tablename:
- @mapperproperty
+ @declared_attr
def __tablename__(cls):
if has_inherited_table(cls):
return None
it on any joined table child classes in addition to their parent
classes::
- from sqlalchemy.ext.declarative import mapperproperty
+ from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.ext.declarative import has_inherited_table
class Tablename:
- @mapperproperty
+ @declared_attr
def __tablename__(cls):
if (has_inherited_table(cls) and
Tablename not in cls.__bases__):
specified with declarative mixins, you may want to combine
some parameters from several mixins with those you wish to
define on the class iteself. The
-:func:`.mapperproperty` decorator can be used
+:func:`.declared_attr` decorator can be used
here to create user-defined collation routines that pull
from multiple collections::
- from sqlalchemy.ext.declarative import mapperproperty
+ from sqlalchemy.ext.declarative import declared_attr
class MySQLSettings:
__table_args__ = {'mysql_engine':'InnoDB'}
class MyModel(Base,MySQLSettings,MyOtherMixin):
__tablename__='my_model'
- @mapperproperty
+ @declared_attr
def __table_args__(self):
args = dict()
args.update(MySQLSettings.__table_args__)
tablename = None
parent_columns = ()
- declarative_props = (mapperproperty, util.classproperty)
+ declarative_props = (declared_attr, util.classproperty)
for base in cls.__mro__:
class_mapped = _is_mapped_class(base)
return comparable_property(comparator_factory, fn)
return decorate
-class mapperproperty(property):
+class declared_attr(property):
"""Mark a class-level method as representing the definition of
a mapped property or special declarative member name.
- .. note:: @mapperproperty is available as
+ .. note:: @declared_attr is available as
sqlalchemy.util.classproperty for SQLAlchemy versions
0.6.2, 0.6.3, 0.6.4.
- @mapperproperty turns the attribute into a scalar-like
+ @declared_attr turns the attribute into a scalar-like
property that can be invoked from the uninstantiated class.
Declarative treats attributes specifically marked with
- @mapperproperty as returning a construct that is specific
+ @declared_attr as returning a construct that is specific
to mapping or declarative table configuration. The name
of the attribute is that of what the non-dynamic version
of the attribute would be.
- @mapperproperty is more often than not applicable to mixins,
+ @declared_attr is more often than not applicable to mixins,
to define relationships that are to be applied to different
implementors of the class::
class ProvidesUser(object):
"A mixin that adds a 'user' relationship to classes."
- @mapperproperty
+ @declared_attr
def user(self):
return relationship("User")
id = Column(Integer, primary_key=True)
type = Column(String(50), nullable=False)
- @mapperproperty
+ @declared_attr
def __tablename__(cls):
return cls.__name__.lower()
- @mapperproperty
+ @declared_attr
def __mapper_args__(cls):
if cls.__name__ == 'Employee':
return {
"""
def __init__(self, fget, *arg, **kw):
- super(mapperproperty, self).__init__(fget, *arg, **kw)
+ super(declared_attr, self).__init__(fget, *arg, **kw)
self.__doc__ = fget.__doc__
def __get__(desc, self, cls):
from sqlalchemy.test.testing import eq_
from sqlalchemy.util import classproperty
from test.orm._base import ComparableEntity, MappedTest
-from sqlalchemy.ext.declarative import mapperproperty
+from sqlalchemy.ext.declarative import declared_attr
class DeclarativeTestBase(testing.TestBase, testing.AssertsExecutionResults):
def setup(self):
eq_(sess.query(User).all(), [User(name='u1', address_count=2,
addresses=[Address(email='one'), Address(email='two')])])
- def test_useless_mapperproperty(self):
+ def test_useless_declared_attr(self):
class Address(Base, ComparableEntity):
__tablename__ = 'addresses'
name = Column('name', String(50))
addresses = relationship('Address', backref='user')
- @mapperproperty
+ @declared_attr
def address_count(cls):
# this doesn't really gain us anything. but if
# one is used, lets have it function as expected...
def test_table_name_inherited(self):
class MyMixin:
- @mapperproperty
+ @declared_attr
def __tablename__(cls):
return cls.__name__.lower()
id = Column(Integer, primary_key=True)
def test_table_name_not_inherited(self):
class MyMixin:
- @mapperproperty
+ @declared_attr
def __tablename__(cls):
return cls.__name__.lower()
id = Column(Integer, primary_key=True)
def test_table_name_inheritance_order(self):
class MyMixin1:
- @mapperproperty
+ @declared_attr
def __tablename__(cls):
return cls.__name__.lower() + '1'
class MyMixin2:
- @mapperproperty
+ @declared_attr
def __tablename__(cls):
return cls.__name__.lower() + '2'
def test_table_name_dependent_on_subclass(self):
class MyHistoryMixin:
- @mapperproperty
+ @declared_attr
def __tablename__(cls):
return cls.parent_name + '_changelog'
def test_table_args_inherited_descriptor(self):
class MyMixin:
- @mapperproperty
+ @declared_attr
def __table_args__(cls):
return {'info': cls.__name__}
eq_(MyModel.__table__.kwargs, {'mysql_engine': 'InnoDB'})
- def test_mapper_args_mapperproperty(self):
+ def test_mapper_args_declared_attr(self):
class ComputedMapperArgs:
- @mapperproperty
+ @declared_attr
def __mapper_args__(cls):
if cls.__name__ == 'Person':
return {'polymorphic_on': cls.discriminator}
is Person.__table__.c.type
eq_(class_mapper(Engineer).polymorphic_identity, 'Engineer')
- def test_mapper_args_mapperproperty_two(self):
+ def test_mapper_args_declared_attr_two(self):
- # same as test_mapper_args_mapperproperty, but we repeat
+ # same as test_mapper_args_declared_attr, but we repeat
# ComputedMapperArgs on both classes for no apparent reason.
class ComputedMapperArgs:
- @mapperproperty
+ @declared_attr
def __mapper_args__(cls):
if cls.__name__ == 'Person':
return {'polymorphic_on': cls.discriminator}
__tablename__ = 'test'
- @mapperproperty
+ @declared_attr
def __table_args__(self):
info = {}
args = dict(info=info)
class MyMixin:
- @mapperproperty
+ @declared_attr
def __mapper_args__(cls):
# tenuous, but illustrates the problem!
__tablename__ = 'test'
- @mapperproperty
+ @declared_attr
def __mapper_args__(cls):
args = {}
args.update(MyMixin1.__mapper_args__)
def test_mapper_args_property(self):
class MyModel(Base):
- @mapperproperty
+ @declared_attr
def __tablename__(cls):
return cls.__name__.lower()
- @mapperproperty
+ @declared_attr
def __table_args__(cls):
return {'mysql_engine':'InnoDB'}
- @mapperproperty
+ @declared_attr
def __mapper_args__(cls):
args = {}
args['polymorphic_identity'] = cls.__name__
eq_(MySubModel.__table__.name, 'mysubmodel')
def test_mapper_args_custom_base(self):
- """test the @mapperproperty approach from a custom base."""
+ """test the @declared_attr approach from a custom base."""
class Base(object):
- @mapperproperty
+ @declared_attr
def __tablename__(cls):
return cls.__name__.lower()
- @mapperproperty
+ @declared_attr
def __table_args__(cls):
return {'mysql_engine':'InnoDB'}
- @mapperproperty
+ @declared_attr
def id(self):
return Column(Integer, primary_key=True)
class CommonMixin:
- @mapperproperty
+ @declared_attr
def __tablename__(cls):
return cls.__name__.lower()
__table_args__ = {'mysql_engine': 'InnoDB'}
class CommonMixin:
- @mapperproperty
+ @declared_attr
def __tablename__(cls):
return cls.__name__.lower()
__table_args__ = {'mysql_engine': 'InnoDB'}
class NoJoinedTableNameMixin:
- @mapperproperty
+ @declared_attr
def __tablename__(cls):
if decl.has_inherited_table(cls):
return None
class TableNameMixin:
- @mapperproperty
+ @declared_attr
def __tablename__(cls):
if decl.has_inherited_table(cls) and TableNameMixin \
not in cls.__bases__:
class MyMixin(object):
- @mapperproperty
+ @declared_attr
def prop_hoho(cls):
return column_property(Column('prop', String(50)))
def test_doc(self):
"""test documentation transfer.
- the documentation situation with @mapperproperty is problematic.
+ the documentation situation with @declared_attr is problematic.
at least see if mapped subclasses get the doc.
"""
class MyMixin(object):
- @mapperproperty
+ @declared_attr
def type_(cls):
"""this is a document."""
return Column(String(50))
- @mapperproperty
+ @declared_attr
def t2(cls):
"""this is another document."""
class MyMixin(object):
- @mapperproperty
+ @declared_attr
def type_(cls):
return Column(String(50))
__mapper_args__ = {'polymorphic_on': type_}
class MyMixin(object):
- @mapperproperty
+ @declared_attr
def data(cls):
return deferred(Column('data', String(50)))
class RefTargetMixin(object):
- @mapperproperty
+ @declared_attr
def target_id(cls):
return Column('target_id', ForeignKey('target.id'))
if usestring:
- @mapperproperty
+ @declared_attr
def target(cls):
return relationship('Target',
primaryjoin='Target.id==%s.target_id'
% cls.__name__)
else:
- @mapperproperty
+ @declared_attr
def target(cls):
return relationship('Target')