* :ref:`correlation_context_specific`
+ .. change::
+ :tags: feature, orm
+ :tickets: 2675
+
+ A meaningful :attr:`.QueryableAttribute.info` attribute is
+ added, which proxies down to the ``.info`` attribute on either
+ the :class:`.schema.Column` object if directly present, or
+ the :class:`.MapperProperty` otherwise. The full behavior
+ is documented and ensured by tests to remain stable.
+
.. change::
:tags: bug, sql
:tickets: 2668
# TODO: conditionally attach this method based on clause_element ?
return self
+
+ @util.memoized_property
+ def info(self):
+ """Return the 'info' dictionary for the underlying SQL element.
+
+ The behavior here is as follows:
+
+ * If the attribute is a column-mapped property, i.e.
+ :class:`.ColumnProperty`, which is mapped directly
+ to a schema-level :class:`.Column` object, this attribute
+ will return the :attr:`.SchemaItem.info` dictionary associated
+ with the core-level :class:`.Column` object.
+
+ * If the attribute is a :class:`.ColumnProperty` but is mapped to
+ any other kind of SQL expression other than a :class:`.Column`,
+ the attribute will refer to the :attr:`.MapperProperty.info` dictionary
+ associated directly with the :class:`.ColumnProperty`, assuming the SQL
+ expression itself does not have it's own ``.info`` attribute
+ (which should be the case, unless a user-defined SQL construct
+ has defined one).
+
+ * If the attribute refers to any other kind of :class:`.MapperProperty`,
+ including :class:`.RelationshipProperty`, the attribute will refer
+ to the :attr:`.MapperProperty.info` dictionary associated with
+ that :class:`.MapperProperty`.
+
+ * To access the :attr:`.MapperProperty.info` dictionary of the :class:`.MapperProperty`
+ unconditionally, including for a :class:`.ColumnProperty` that's
+ associated directly with a :class:`.schema.Column`, the attribute
+ can be referred to using :attr:`.QueryableAttribute.property`
+ attribute, as ``MyClass.someattribute.property.info``.
+
+ .. versionadded:: 0.8.0
+
+ .. seealso::
+
+ :attr:`.SchemaItem.info`
+
+ :attr:`.MapperProperty.info`
+
+ """
+ return self.comparator.info
+
@util.memoized_property
def parent(self):
"""Return an inspection instance representing the parent.
.. versionadded:: 0.8 Added support for .info to all
:class:`.MapperProperty` subclasses.
+ .. seealso::
+
+ :attr:`.QueryableAttribute.info`
+
+ :attr:`.SchemaItem.info`
+
"""
return {}
return self.__class__(self.prop, self._parentmapper, adapter)
+ @util.memoized_property
+ def info(self):
+ return self.property.info
+
@staticmethod
def any_op(a, b, **kwargs):
return a.any(b, **kwargs)
"parententity": self._parentmapper,
"parentmapper": self._parentmapper})
+ @util.memoized_property
+ def info(self):
+ ce = self.__clause_element__()
+ try:
+ return ce.info
+ except AttributeError:
+ return self.prop.info
+
def __getattr__(self, key):
"""proxy attribute access down to the mapped column.
"""pull 'key' from parent, if not present"""
return self._Annotated__element.key
+ @util.memoized_property
+ def info(self):
+ return self._Annotated__element.info
# hard-generate Annotated subclasses. this technique
# is used instead of on-the-fly types (i.e. type.__new__())
obj.info["q"] = "p"
eq_(obj.info, {"q": "p"})
+ def test_info_via_instrumented(self):
+ m = MetaData()
+ # create specific tables here as we don't want
+ # users.c.id.info to be pre-initialized
+ users = Table('u', m, Column('id', Integer, primary_key=True),
+ Column('name', String))
+ addresses = Table('a', m, Column('id', Integer, primary_key=True),
+ Column('name', String),
+ Column('user_id', Integer, ForeignKey('u.id')))
+ Address = self.classes.Address
+ User = self.classes.User
+
+ mapper(User, users, properties={
+ "name_lower": column_property(func.lower(users.c.name)),
+ "addresses": relationship(Address)
+ })
+ mapper(Address, addresses)
+
+ # attr.info goes down to the original Column object
+ # for the dictionary. The annotated element needs to pass
+ # this on.
+ assert 'info' not in users.c.id.__dict__
+ is_(User.id.info, users.c.id.info)
+ assert 'info' in users.c.id.__dict__
+
+ # for SQL expressions, ORM-level .info
+ is_(User.name_lower.info, User.name_lower.property.info)
+
+ # same for relationships
+ is_(User.addresses.info, User.addresses.property.info)
+
def test_add_property(self):
users, addresses, Address = (self.tables.users,