--- /dev/null
+.. change::
+ :tags: usecase, schema
+ :tickets: 5712
+
+ The :meth:`_events.DDLEvents.column_reflect` event may now be applied to a
+ :class:`_schema.MetaData` object where it will take effect for the
+ :class:`_schema.Table` objects local to that collection.
+
+ .. seealso::
+
+ :meth:`_events.DDLEvents.column_reflect`
+
+ :ref:`mapper_automated_reflection_schemes` - in the ORM mapping documentation
+
+ :ref:`automap_intercepting_columns` - in the :ref:`automap_toplevel` documentation
+
+
+
name than the column. But what if we aren't listing out :class:`_schema.Column`
objects explicitly, and instead are automating the production of :class:`_schema.Table`
objects using reflection (e.g. as described in :ref:`metadata_reflection_toplevel`)?
-In this case we can make use of the :meth:`.DDLEvents.column_reflect` event
+In this case we can make use of the :meth:`_events.DDLEvents.column_reflect` event
to intercept the production of :class:`_schema.Column` objects and provide them
-with the :attr:`_schema.Column.key` of our choice::
+with the :attr:`_schema.Column.key` of our choice. The event is most easily
+associated with the :class:`_schema.MetaData` object that's in use,
+such as below we use the one linked to the :class:`_orm.declarative_base`
+instance::
- @event.listens_for(Table, "column_reflect")
+ @event.listens_for(Base.metadata, "column_reflect")
def column_reflect(inspector, table, column_info):
# set column.key = "attr_<lower_case_name>"
column_info['key'] = "attr_%s" % column_info['name'].lower()
__table__ = Table("some_table", Base.metadata,
autoload_with=some_engine)
-If we want to qualify our event to only react for the specific :class:`_schema.MetaData`
-object above, we can check for it in our event::
+The approach also works with the :ref:`automap_toplevel` extension. See
+the section :ref:`automap_intercepting_columns` for background.
- @event.listens_for(Table, "column_reflect")
- def column_reflect(inspector, table, column_info):
- if table.metadata is Base.metadata:
- # set column.key = "attr_<lower_case_name>"
- column_info['key'] = "attr_%s" % column_info['name'].lower()
+.. seealso::
+
+ :meth:`_events.DDLEvents.column_reflect`
+
+ :ref:`automap_intercepting_columns` - in the :ref:`automap_toplevel` documentation
.. _column_prefix:
orig_name = col_d["name"]
+ table.metadata.dispatch.column_reflect(self, table, col_d)
table.dispatch.column_reflect(self, table, col_d)
# fetch name again as column_reflect is allowed to
we've declared are in an un-mapped state.
+.. _automap_intercepting_columns:
+
+Intercepting Column Definitions
+===============================
+
+The :class:`_schema.MetaData` and :class:`_schema.Table` objects support an
+event hook :meth:`_events.DDLEvents.column_reflect` that may be used to intercept
+the information reflected about a database column before the :class:`_schema.Column`
+object is constructed. For example if we wanted to map columns using a
+naming convention such as ``"attr_<columnname>"``, the event could
+be applied as::
+
+ @event.listens_for(Base.metadata, "column_reflect")
+ def column_reflect(inspector, table, column_info):
+ # set column.key = "attr_<lower_case_name>"
+ column_info['key'] = "attr_%s" % column_info['name'].lower()
+
+ # run reflection
+ Base.prepare(engine, reflect=True)
+
+.. versionadded:: 1.4.0b2 the :meth:`_events.DDLEvents.column_reflect` event
+ may be applied to a :class:`_schema.MetaData` object.
+
+.. seealso::
+
+ :meth:`_events.DDLEvents.column_reflect`
+
+ :ref:`mapper_automated_reflection_schemes` - in the ORM mapping documentation
+
+
""" # noqa
from .declarative import declarative_base as _declarative_base
from .. import util
"""Called for each unit of 'column info' retrieved when
a :class:`_schema.Table` is being reflected.
- Currently, this event may only be applied to the :class:`_schema.Table`
- class directly::
+ This event is most easily used by applying it to a specific
+ :class:`_schema.MetaData` instance, where it will take effect for
+ all :class:`_schema.Table` objects within that
+ :class:`_schema.MetaData` that undergo reflection::
+
+ metadata = MetaData()
+
+ @event.listens_for(metadata, 'column_reflect')
+ def receive_column_reflect(inspector, table, column_info):
+ # receives for all Table objects that are reflected
+ # under this MetaData
+
+
+ # will use the above event hook
+ my_table = Table("my_table", metadata, autoload_with=some_engine)
+
+
+ .. versionadded:: 1.4.0b2 The :meth:`_events.DDLEvents.column_reflect`
+ hook may now be applied to a :class:`_schema.MetaData` object as
+ well as the :class:`_schema.MetaData` class itself where it will
+ take place for all :class:`_schema.Table` objects associated with
+ the targeted :class:`_schema.MetaData`.
+
+ It may also be applied to the :class:`_schema.Table` class across
+ the board::
from sqlalchemy import Table
def receive_column_reflect(inspector, table, column_info):
# receives for all Table objects that are reflected
- Or applied using the
+ It can also be applied to a specific :class:`_schema.Table` at the
+ point that one is being reflected using the
:paramref:`_schema.Table.listeners` parameter::
t1 = Table(
or :func:`_expression.text` object as well. Is applied to the
:paramref:`_schema.Column.server_default` parameter
- .. versionchanged:: 1.1.6
-
- The :meth:`.DDLEvents.column_reflect` event allows a non
- string :class:`.FetchedValue`,
- :func:`_expression.text`, or derived object to be
- specified as the value of ``default`` in the column
- dictionary.
-
The event is called before any action is taken against
this dictionary, and the contents can be modified; the following
additional keys may be added to the dictionary to further modify
i.e. those copies that are generated when
:meth:`_schema.Table.to_metadata` is used.
+ .. seealso::
+
+ :ref:`mapper_automated_reflection_schemes` -
+ in the ORM mapping documentation
+
+ :ref:`automap_intercepting_columns` -
+ in the :ref:`automap_toplevel` documentation
+
"""
which will be passed to :func:`.event.listen` upon construction.
This alternate hook to :func:`.event.listen` allows the establishment
of a listener function specific to this :class:`_schema.Table` before
- the "autoload" process begins. Particularly useful for
- the :meth:`.DDLEvents.column_reflect` event::
+ the "autoload" process begins. Historically this has been intended
+ for use with the :meth:`.DDLEvents.column_reflect` event, however
+ note that this event hook may now be associated with the
+ :class:`_schema.MetaData` object directly::
def listen_for_reflect(table, column_info):
"handle the column reflection event"
('column_reflect', listen_for_reflect)
])
+ .. seealso::
+
+ :meth:`_events.DDLEvents.column_reflect`
+
:param must_exist: When ``True``, indicates that this Table must already
be present in the given :class:`_schema.MetaData` collection, else
an exception is raised.
import sqlalchemy as sa
from sqlalchemy import Computed
from sqlalchemy import DefaultClause
+from sqlalchemy import event
from sqlalchemy import FetchedValue
from sqlalchemy import ForeignKey
from sqlalchemy import Identity
self._do_test("x", {"default": my_default}, assert_text_of_one)
+ def test_listen_metadata_obj(self):
+ m1 = MetaData()
+
+ m2 = MetaData()
+
+ canary = []
+
+ @event.listens_for(m1, "column_reflect")
+ def go(insp, table, info):
+ canary.append(info["name"])
+
+ Table("related", m1, autoload_with=testing.db)
+
+ Table("related", m2, autoload_with=testing.db)
+
+ eq_(canary, ["q", "x", "y"])
+
+ def test_listen_metadata_cls(self):
+ m1 = MetaData()
+
+ m2 = MetaData()
+
+ canary = []
+
+ def go(insp, table, info):
+ canary.append(info["name"])
+
+ self.event_listen(MetaData, "column_reflect", go)
+
+ Table("related", m1, autoload_with=testing.db)
+
+ Table("related", m2, autoload_with=testing.db)
+
+ eq_(canary, ["q", "x", "y", "q", "x", "y"])
+
class ComputedColumnTest(fixtures.ComputedReflectionFixtureTest):
def check_table_column(self, table, name, text, persisted):