mirror of
https://github.com/sqlalchemy/sqlalchemy.git
synced 2026-05-29 12:06:28 -04:00
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. [ticket:2675]
This commit is contained in:
Vendored
+10
@@ -18,6 +18,16 @@
|
||||
|
||||
* :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
|
||||
|
||||
@@ -174,6 +174,49 @@ class QueryableAttribute(interfaces._MappedAttribute,
|
||||
# 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.
|
||||
|
||||
@@ -209,6 +209,12 @@ class MapperProperty(_MappedAttribute, _InspectionAttr):
|
||||
.. versionadded:: 0.8 Added support for .info to all
|
||||
:class:`.MapperProperty` subclasses.
|
||||
|
||||
.. seealso::
|
||||
|
||||
:attr:`.QueryableAttribute.info`
|
||||
|
||||
:attr:`.SchemaItem.info`
|
||||
|
||||
"""
|
||||
return {}
|
||||
|
||||
@@ -390,6 +396,10 @@ class PropComparator(operators.ColumnOperators):
|
||||
|
||||
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)
|
||||
|
||||
@@ -193,6 +193,14 @@ class ColumnProperty(StrategizedProperty):
|
||||
"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.
|
||||
|
||||
|
||||
@@ -507,6 +507,9 @@ class AnnotatedColumnElement(Annotated):
|
||||
"""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__())
|
||||
|
||||
@@ -407,6 +407,37 @@ class MapperTest(_fixtures.FixtureTest, AssertsCompiledSQL):
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user