events.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. # sql/events.py
  2. # Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
  3. # <see AUTHORS file>
  4. #
  5. # This module is part of SQLAlchemy and is released under
  6. # the MIT License: https://www.opensource.org/licenses/mit-license.php
  7. from __future__ import annotations
  8. from typing import Any
  9. from typing import TYPE_CHECKING
  10. from .base import SchemaEventTarget
  11. from .. import event
  12. if TYPE_CHECKING:
  13. from .schema import Column
  14. from .schema import Constraint
  15. from .schema import SchemaItem
  16. from .schema import Table
  17. from ..engine.base import Connection
  18. from ..engine.interfaces import ReflectedColumn
  19. from ..engine.reflection import Inspector
  20. class DDLEvents(event.Events[SchemaEventTarget]):
  21. """
  22. Define event listeners for schema objects,
  23. that is, :class:`.SchemaItem` and other :class:`.SchemaEventTarget`
  24. subclasses, including :class:`_schema.MetaData`, :class:`_schema.Table`,
  25. :class:`_schema.Column`, etc.
  26. **Create / Drop Events**
  27. Events emitted when CREATE and DROP commands are emitted to the database.
  28. The event hooks in this category include :meth:`.DDLEvents.before_create`,
  29. :meth:`.DDLEvents.after_create`, :meth:`.DDLEvents.before_drop`, and
  30. :meth:`.DDLEvents.after_drop`.
  31. These events are emitted when using schema-level methods such as
  32. :meth:`.MetaData.create_all` and :meth:`.MetaData.drop_all`. Per-object
  33. create/drop methods such as :meth:`.Table.create`, :meth:`.Table.drop`,
  34. :meth:`.Index.create` are also included, as well as dialect-specific
  35. methods such as :meth:`_postgresql.ENUM.create`.
  36. .. versionadded:: 2.0 :class:`.DDLEvents` event hooks now take place
  37. for non-table objects including constraints, indexes, and
  38. dialect-specific schema types.
  39. Event hooks may be attached directly to a :class:`_schema.Table` object or
  40. to a :class:`_schema.MetaData` collection, as well as to any
  41. :class:`.SchemaItem` class or object that can be individually created and
  42. dropped using a distinct SQL command. Such classes include :class:`.Index`,
  43. :class:`.Sequence`, and dialect-specific classes such as
  44. :class:`_postgresql.ENUM`.
  45. Example using the :meth:`.DDLEvents.after_create` event, where a custom
  46. event hook will emit an ``ALTER TABLE`` command on the current connection,
  47. after ``CREATE TABLE`` is emitted::
  48. from sqlalchemy import create_engine
  49. from sqlalchemy import event
  50. from sqlalchemy import Table, Column, Metadata, Integer
  51. m = MetaData()
  52. some_table = Table("some_table", m, Column("data", Integer))
  53. @event.listens_for(some_table, "after_create")
  54. def after_create(target, connection, **kw):
  55. connection.execute(
  56. text("ALTER TABLE %s SET name=foo_%s" % (target.name, target.name))
  57. )
  58. some_engine = create_engine("postgresql://scott:tiger@host/test")
  59. # will emit "CREATE TABLE some_table" as well as the above
  60. # "ALTER TABLE" statement afterwards
  61. m.create_all(some_engine)
  62. Constraint objects such as :class:`.ForeignKeyConstraint`,
  63. :class:`.UniqueConstraint`, :class:`.CheckConstraint` may also be
  64. subscribed to these events, however they will **not** normally produce
  65. events as these objects are usually rendered inline within an
  66. enclosing ``CREATE TABLE`` statement and implicitly dropped from a
  67. ``DROP TABLE`` statement.
  68. For the :class:`.Index` construct, the event hook will be emitted
  69. for ``CREATE INDEX``, however SQLAlchemy does not normally emit
  70. ``DROP INDEX`` when dropping tables as this is again implicit within the
  71. ``DROP TABLE`` statement.
  72. .. versionadded:: 2.0 Support for :class:`.SchemaItem` objects
  73. for create/drop events was expanded from its previous support for
  74. :class:`.MetaData` and :class:`.Table` to also include
  75. :class:`.Constraint` and all subclasses, :class:`.Index`,
  76. :class:`.Sequence` and some type-related constructs such as
  77. :class:`_postgresql.ENUM`.
  78. .. note:: These event hooks are only emitted within the scope of
  79. SQLAlchemy's create/drop methods; they are not necessarily supported
  80. by tools such as `alembic <https://alembic.sqlalchemy.org>`_.
  81. **Attachment Events**
  82. Attachment events are provided to customize
  83. behavior whenever a child schema element is associated
  84. with a parent, such as when a :class:`_schema.Column` is associated
  85. with its :class:`_schema.Table`, when a
  86. :class:`_schema.ForeignKeyConstraint`
  87. is associated with a :class:`_schema.Table`, etc. These events include
  88. :meth:`.DDLEvents.before_parent_attach` and
  89. :meth:`.DDLEvents.after_parent_attach`.
  90. **Reflection Events**
  91. The :meth:`.DDLEvents.column_reflect` event is used to intercept
  92. and modify the in-Python definition of database columns when
  93. :term:`reflection` of database tables proceeds.
  94. **Use with Generic DDL**
  95. DDL events integrate closely with the
  96. :class:`.DDL` class and the :class:`.ExecutableDDLElement` hierarchy
  97. of DDL clause constructs, which are themselves appropriate
  98. as listener callables::
  99. from sqlalchemy import DDL
  100. event.listen(
  101. some_table,
  102. "after_create",
  103. DDL("ALTER TABLE %(table)s SET name=foo_%(table)s"),
  104. )
  105. **Event Propagation to MetaData Copies**
  106. For all :class:`.DDLEvent` events, the ``propagate=True`` keyword argument
  107. will ensure that a given event handler is propagated to copies of the
  108. object, which are made when using the :meth:`_schema.Table.to_metadata`
  109. method::
  110. from sqlalchemy import DDL
  111. metadata = MetaData()
  112. some_table = Table("some_table", metadata, Column("data", Integer))
  113. event.listen(
  114. some_table,
  115. "after_create",
  116. DDL("ALTER TABLE %(table)s SET name=foo_%(table)s"),
  117. propagate=True,
  118. )
  119. new_metadata = MetaData()
  120. new_table = some_table.to_metadata(new_metadata)
  121. The above :class:`.DDL` object will be associated with the
  122. :meth:`.DDLEvents.after_create` event for both the ``some_table`` and
  123. the ``new_table`` :class:`.Table` objects.
  124. .. seealso::
  125. :ref:`event_toplevel`
  126. :class:`.ExecutableDDLElement`
  127. :class:`.DDL`
  128. :ref:`schema_ddl_sequences`
  129. """ # noqa: E501
  130. _target_class_doc = "SomeSchemaClassOrObject"
  131. _dispatch_target = SchemaEventTarget
  132. def before_create(
  133. self, target: SchemaEventTarget, connection: Connection, **kw: Any
  134. ) -> None:
  135. r"""Called before CREATE statements are emitted.
  136. :param target: the :class:`.SchemaObject`, such as a
  137. :class:`_schema.MetaData` or :class:`_schema.Table`
  138. but also including all create/drop objects such as
  139. :class:`.Index`, :class:`.Sequence`, etc.,
  140. object which is the target of the event.
  141. .. versionadded:: 2.0 Support for all :class:`.SchemaItem` objects
  142. was added.
  143. :param connection: the :class:`_engine.Connection` where the
  144. CREATE statement or statements will be emitted.
  145. :param \**kw: additional keyword arguments relevant
  146. to the event. The contents of this dictionary
  147. may vary across releases, and include the
  148. list of tables being generated for a metadata-level
  149. event, the checkfirst flag, and other
  150. elements used by internal events.
  151. :func:`.event.listen` accepts the ``propagate=True``
  152. modifier for this event; when True, the listener function will
  153. be established for any copies made of the target object,
  154. i.e. those copies that are generated when
  155. :meth:`_schema.Table.to_metadata` is used.
  156. :func:`.event.listen` accepts the ``insert=True``
  157. modifier for this event; when True, the listener function will
  158. be prepended to the internal list of events upon discovery, and execute
  159. before registered listener functions that do not pass this argument.
  160. """
  161. def after_create(
  162. self, target: SchemaEventTarget, connection: Connection, **kw: Any
  163. ) -> None:
  164. r"""Called after CREATE statements are emitted.
  165. :param target: the :class:`.SchemaObject`, such as a
  166. :class:`_schema.MetaData` or :class:`_schema.Table`
  167. but also including all create/drop objects such as
  168. :class:`.Index`, :class:`.Sequence`, etc.,
  169. object which is the target of the event.
  170. .. versionadded:: 2.0 Support for all :class:`.SchemaItem` objects
  171. was added.
  172. :param connection: the :class:`_engine.Connection` where the
  173. CREATE statement or statements have been emitted.
  174. :param \**kw: additional keyword arguments relevant
  175. to the event. The contents of this dictionary
  176. may vary across releases, and include the
  177. list of tables being generated for a metadata-level
  178. event, the checkfirst flag, and other
  179. elements used by internal events.
  180. :func:`.event.listen` also accepts the ``propagate=True``
  181. modifier for this event; when True, the listener function will
  182. be established for any copies made of the target object,
  183. i.e. those copies that are generated when
  184. :meth:`_schema.Table.to_metadata` is used.
  185. """
  186. def before_drop(
  187. self, target: SchemaEventTarget, connection: Connection, **kw: Any
  188. ) -> None:
  189. r"""Called before DROP statements are emitted.
  190. :param target: the :class:`.SchemaObject`, such as a
  191. :class:`_schema.MetaData` or :class:`_schema.Table`
  192. but also including all create/drop objects such as
  193. :class:`.Index`, :class:`.Sequence`, etc.,
  194. object which is the target of the event.
  195. .. versionadded:: 2.0 Support for all :class:`.SchemaItem` objects
  196. was added.
  197. :param connection: the :class:`_engine.Connection` where the
  198. DROP statement or statements will be emitted.
  199. :param \**kw: additional keyword arguments relevant
  200. to the event. The contents of this dictionary
  201. may vary across releases, and include the
  202. list of tables being generated for a metadata-level
  203. event, the checkfirst flag, and other
  204. elements used by internal events.
  205. :func:`.event.listen` also accepts the ``propagate=True``
  206. modifier for this event; when True, the listener function will
  207. be established for any copies made of the target object,
  208. i.e. those copies that are generated when
  209. :meth:`_schema.Table.to_metadata` is used.
  210. """
  211. def after_drop(
  212. self, target: SchemaEventTarget, connection: Connection, **kw: Any
  213. ) -> None:
  214. r"""Called after DROP statements are emitted.
  215. :param target: the :class:`.SchemaObject`, such as a
  216. :class:`_schema.MetaData` or :class:`_schema.Table`
  217. but also including all create/drop objects such as
  218. :class:`.Index`, :class:`.Sequence`, etc.,
  219. object which is the target of the event.
  220. .. versionadded:: 2.0 Support for all :class:`.SchemaItem` objects
  221. was added.
  222. :param connection: the :class:`_engine.Connection` where the
  223. DROP statement or statements have been emitted.
  224. :param \**kw: additional keyword arguments relevant
  225. to the event. The contents of this dictionary
  226. may vary across releases, and include the
  227. list of tables being generated for a metadata-level
  228. event, the checkfirst flag, and other
  229. elements used by internal events.
  230. :func:`.event.listen` also accepts the ``propagate=True``
  231. modifier for this event; when True, the listener function will
  232. be established for any copies made of the target object,
  233. i.e. those copies that are generated when
  234. :meth:`_schema.Table.to_metadata` is used.
  235. """
  236. def before_parent_attach(
  237. self, target: SchemaEventTarget, parent: SchemaItem
  238. ) -> None:
  239. """Called before a :class:`.SchemaItem` is associated with
  240. a parent :class:`.SchemaItem`.
  241. :param target: the target object
  242. :param parent: the parent to which the target is being attached.
  243. :func:`.event.listen` also accepts the ``propagate=True``
  244. modifier for this event; when True, the listener function will
  245. be established for any copies made of the target object,
  246. i.e. those copies that are generated when
  247. :meth:`_schema.Table.to_metadata` is used.
  248. """
  249. def after_parent_attach(
  250. self, target: SchemaEventTarget, parent: SchemaItem
  251. ) -> None:
  252. """Called after a :class:`.SchemaItem` is associated with
  253. a parent :class:`.SchemaItem`.
  254. :param target: the target object
  255. :param parent: the parent to which the target is being attached.
  256. :func:`.event.listen` also accepts the ``propagate=True``
  257. modifier for this event; when True, the listener function will
  258. be established for any copies made of the target object,
  259. i.e. those copies that are generated when
  260. :meth:`_schema.Table.to_metadata` is used.
  261. """
  262. def _sa_event_column_added_to_pk_constraint(
  263. self, const: Constraint, col: Column[Any]
  264. ) -> None:
  265. """internal event hook used for primary key naming convention
  266. updates.
  267. """
  268. def column_reflect(
  269. self, inspector: Inspector, table: Table, column_info: ReflectedColumn
  270. ) -> None:
  271. """Called for each unit of 'column info' retrieved when
  272. a :class:`_schema.Table` is being reflected.
  273. This event is most easily used by applying it to a specific
  274. :class:`_schema.MetaData` instance, where it will take effect for
  275. all :class:`_schema.Table` objects within that
  276. :class:`_schema.MetaData` that undergo reflection::
  277. metadata = MetaData()
  278. @event.listens_for(metadata, "column_reflect")
  279. def receive_column_reflect(inspector, table, column_info):
  280. # receives for all Table objects that are reflected
  281. # under this MetaData
  282. ...
  283. # will use the above event hook
  284. my_table = Table("my_table", metadata, autoload_with=some_engine)
  285. .. versionadded:: 1.4.0b2 The :meth:`_events.DDLEvents.column_reflect`
  286. hook may now be applied to a :class:`_schema.MetaData` object as
  287. well as the :class:`_schema.MetaData` class itself where it will
  288. take place for all :class:`_schema.Table` objects associated with
  289. the targeted :class:`_schema.MetaData`.
  290. It may also be applied to the :class:`_schema.Table` class across
  291. the board::
  292. from sqlalchemy import Table
  293. @event.listens_for(Table, "column_reflect")
  294. def receive_column_reflect(inspector, table, column_info):
  295. # receives for all Table objects that are reflected
  296. ...
  297. It can also be applied to a specific :class:`_schema.Table` at the
  298. point that one is being reflected using the
  299. :paramref:`_schema.Table.listeners` parameter::
  300. t1 = Table(
  301. "my_table",
  302. autoload_with=some_engine,
  303. listeners=[("column_reflect", receive_column_reflect)],
  304. )
  305. The dictionary of column information as returned by the
  306. dialect is passed, and can be modified. The dictionary
  307. is that returned in each element of the list returned
  308. by :meth:`.reflection.Inspector.get_columns`:
  309. * ``name`` - the column's name, is applied to the
  310. :paramref:`_schema.Column.name` parameter
  311. * ``type`` - the type of this column, which should be an instance
  312. of :class:`~sqlalchemy.types.TypeEngine`, is applied to the
  313. :paramref:`_schema.Column.type` parameter
  314. * ``nullable`` - boolean flag if the column is NULL or NOT NULL,
  315. is applied to the :paramref:`_schema.Column.nullable` parameter
  316. * ``default`` - the column's server default value. This is
  317. normally specified as a plain string SQL expression, however the
  318. event can pass a :class:`.FetchedValue`, :class:`.DefaultClause`,
  319. or :func:`_expression.text` object as well. Is applied to the
  320. :paramref:`_schema.Column.server_default` parameter
  321. The event is called before any action is taken against
  322. this dictionary, and the contents can be modified; the following
  323. additional keys may be added to the dictionary to further modify
  324. how the :class:`_schema.Column` is constructed:
  325. * ``key`` - the string key that will be used to access this
  326. :class:`_schema.Column` in the ``.c`` collection; will be applied
  327. to the :paramref:`_schema.Column.key` parameter. Is also used
  328. for ORM mapping. See the section
  329. :ref:`mapper_automated_reflection_schemes` for an example.
  330. * ``quote`` - force or un-force quoting on the column name;
  331. is applied to the :paramref:`_schema.Column.quote` parameter.
  332. * ``info`` - a dictionary of arbitrary data to follow along with
  333. the :class:`_schema.Column`, is applied to the
  334. :paramref:`_schema.Column.info` parameter.
  335. :func:`.event.listen` also accepts the ``propagate=True``
  336. modifier for this event; when True, the listener function will
  337. be established for any copies made of the target object,
  338. i.e. those copies that are generated when
  339. :meth:`_schema.Table.to_metadata` is used.
  340. .. seealso::
  341. :ref:`mapper_automated_reflection_schemes` -
  342. in the ORM mapping documentation
  343. :ref:`automap_intercepting_columns` -
  344. in the :ref:`automap_toplevel` documentation
  345. :ref:`metadata_reflection_dbagnostic_types` - in
  346. the :ref:`metadata_reflection_toplevel` documentation
  347. """