SQLAlchemy 2.1.0rc1__cp315-cp315-win32.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. sqlalchemy/__init__.py +299 -0
  2. sqlalchemy/connectors/__init__.py +18 -0
  3. sqlalchemy/connectors/aioodbc.py +171 -0
  4. sqlalchemy/connectors/asyncio.py +476 -0
  5. sqlalchemy/connectors/pyodbc.py +248 -0
  6. sqlalchemy/dialects/__init__.py +62 -0
  7. sqlalchemy/dialects/_typing.py +29 -0
  8. sqlalchemy/dialects/mssql/__init__.py +88 -0
  9. sqlalchemy/dialects/mssql/aioodbc.py +63 -0
  10. sqlalchemy/dialects/mssql/base.py +4833 -0
  11. sqlalchemy/dialects/mssql/information_schema.py +345 -0
  12. sqlalchemy/dialects/mssql/json.py +140 -0
  13. sqlalchemy/dialects/mssql/mssqlpython.py +242 -0
  14. sqlalchemy/dialects/mssql/provision.py +196 -0
  15. sqlalchemy/dialects/mssql/pymssql.py +130 -0
  16. sqlalchemy/dialects/mssql/pyodbc.py +697 -0
  17. sqlalchemy/dialects/mysql/__init__.py +106 -0
  18. sqlalchemy/dialects/mysql/_mariadb_shim.py +312 -0
  19. sqlalchemy/dialects/mysql/aiomysql.py +260 -0
  20. sqlalchemy/dialects/mysql/asyncmy.py +241 -0
  21. sqlalchemy/dialects/mysql/base.py +3896 -0
  22. sqlalchemy/dialects/mysql/cymysql.py +107 -0
  23. sqlalchemy/dialects/mysql/dml.py +279 -0
  24. sqlalchemy/dialects/mysql/enumerated.py +277 -0
  25. sqlalchemy/dialects/mysql/expression.py +146 -0
  26. sqlalchemy/dialects/mysql/json.py +92 -0
  27. sqlalchemy/dialects/mysql/mariadb.py +67 -0
  28. sqlalchemy/dialects/mysql/mariadbconnector.py +314 -0
  29. sqlalchemy/dialects/mysql/mysqlconnector.py +291 -0
  30. sqlalchemy/dialects/mysql/mysqldb.py +318 -0
  31. sqlalchemy/dialects/mysql/provision.py +153 -0
  32. sqlalchemy/dialects/mysql/pymysql.py +188 -0
  33. sqlalchemy/dialects/mysql/pyodbc.py +157 -0
  34. sqlalchemy/dialects/mysql/reflection.py +724 -0
  35. sqlalchemy/dialects/mysql/reserved_words.py +570 -0
  36. sqlalchemy/dialects/mysql/types.py +845 -0
  37. sqlalchemy/dialects/oracle/__init__.py +85 -0
  38. sqlalchemy/dialects/oracle/base.py +3847 -0
  39. sqlalchemy/dialects/oracle/cx_oracle.py +1736 -0
  40. sqlalchemy/dialects/oracle/dictionary.py +507 -0
  41. sqlalchemy/dialects/oracle/json.py +157 -0
  42. sqlalchemy/dialects/oracle/oracledb.py +898 -0
  43. sqlalchemy/dialects/oracle/provision.py +288 -0
  44. sqlalchemy/dialects/oracle/types.py +367 -0
  45. sqlalchemy/dialects/oracle/vector.py +366 -0
  46. sqlalchemy/dialects/postgresql/__init__.py +170 -0
  47. sqlalchemy/dialects/postgresql/_psycopg_common.py +232 -0
  48. sqlalchemy/dialects/postgresql/array.py +534 -0
  49. sqlalchemy/dialects/postgresql/asyncpg.py +1318 -0
  50. sqlalchemy/dialects/postgresql/base.py +5935 -0
  51. sqlalchemy/dialects/postgresql/bitstring.py +327 -0
  52. sqlalchemy/dialects/postgresql/dml.py +360 -0
  53. sqlalchemy/dialects/postgresql/ext.py +599 -0
  54. sqlalchemy/dialects/postgresql/hstore.py +422 -0
  55. sqlalchemy/dialects/postgresql/json.py +411 -0
  56. sqlalchemy/dialects/postgresql/named_types.py +535 -0
  57. sqlalchemy/dialects/postgresql/operators.py +129 -0
  58. sqlalchemy/dialects/postgresql/pg8000.py +655 -0
  59. sqlalchemy/dialects/postgresql/pg_catalog.py +345 -0
  60. sqlalchemy/dialects/postgresql/provision.py +202 -0
  61. sqlalchemy/dialects/postgresql/psycopg.py +800 -0
  62. sqlalchemy/dialects/postgresql/psycopg2.py +860 -0
  63. sqlalchemy/dialects/postgresql/psycopg2cffi.py +62 -0
  64. sqlalchemy/dialects/postgresql/ranges.py +1002 -0
  65. sqlalchemy/dialects/postgresql/types.py +388 -0
  66. sqlalchemy/dialects/sqlite/__init__.py +59 -0
  67. sqlalchemy/dialects/sqlite/aiosqlite.py +375 -0
  68. sqlalchemy/dialects/sqlite/base.py +3103 -0
  69. sqlalchemy/dialects/sqlite/dml.py +314 -0
  70. sqlalchemy/dialects/sqlite/json.py +134 -0
  71. sqlalchemy/dialects/sqlite/provision.py +237 -0
  72. sqlalchemy/dialects/sqlite/pysqlcipher.py +166 -0
  73. sqlalchemy/dialects/sqlite/pysqlite.py +959 -0
  74. sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
  75. sqlalchemy/engine/__init__.py +62 -0
  76. sqlalchemy/engine/_processors_cy.cp315-win32.pyd +0 -0
  77. sqlalchemy/engine/_processors_cy.py +92 -0
  78. sqlalchemy/engine/_result_cy.cp315-win32.pyd +0 -0
  79. sqlalchemy/engine/_result_cy.py +711 -0
  80. sqlalchemy/engine/_row_cy.cp315-win32.pyd +0 -0
  81. sqlalchemy/engine/_row_cy.py +232 -0
  82. sqlalchemy/engine/_util_cy.cp315-win32.pyd +0 -0
  83. sqlalchemy/engine/_util_cy.py +136 -0
  84. sqlalchemy/engine/base.py +3357 -0
  85. sqlalchemy/engine/characteristics.py +155 -0
  86. sqlalchemy/engine/create.py +877 -0
  87. sqlalchemy/engine/cursor.py +2425 -0
  88. sqlalchemy/engine/default.py +2627 -0
  89. sqlalchemy/engine/events.py +965 -0
  90. sqlalchemy/engine/interfaces.py +3636 -0
  91. sqlalchemy/engine/mock.py +133 -0
  92. sqlalchemy/engine/processors.py +83 -0
  93. sqlalchemy/engine/reflection.py +2141 -0
  94. sqlalchemy/engine/result.py +2012 -0
  95. sqlalchemy/engine/row.py +397 -0
  96. sqlalchemy/engine/strategies.py +16 -0
  97. sqlalchemy/engine/url.py +922 -0
  98. sqlalchemy/engine/util.py +164 -0
  99. sqlalchemy/event/__init__.py +26 -0
  100. sqlalchemy/event/api.py +220 -0
  101. sqlalchemy/event/attr.py +675 -0
  102. sqlalchemy/event/base.py +473 -0
  103. sqlalchemy/event/legacy.py +259 -0
  104. sqlalchemy/event/registry.py +391 -0
  105. sqlalchemy/events.py +17 -0
  106. sqlalchemy/exc.py +939 -0
  107. sqlalchemy/ext/__init__.py +10 -0
  108. sqlalchemy/ext/associationproxy.py +2073 -0
  109. sqlalchemy/ext/asyncio/__init__.py +29 -0
  110. sqlalchemy/ext/asyncio/base.py +281 -0
  111. sqlalchemy/ext/asyncio/engine.py +1487 -0
  112. sqlalchemy/ext/asyncio/exc.py +21 -0
  113. sqlalchemy/ext/asyncio/result.py +994 -0
  114. sqlalchemy/ext/asyncio/scoping.py +1679 -0
  115. sqlalchemy/ext/asyncio/session.py +2006 -0
  116. sqlalchemy/ext/automap.py +1702 -0
  117. sqlalchemy/ext/baked.py +558 -0
  118. sqlalchemy/ext/compiler.py +601 -0
  119. sqlalchemy/ext/declarative/__init__.py +65 -0
  120. sqlalchemy/ext/declarative/extensions.py +561 -0
  121. sqlalchemy/ext/horizontal_shard.py +481 -0
  122. sqlalchemy/ext/hybrid.py +1877 -0
  123. sqlalchemy/ext/indexable.py +364 -0
  124. sqlalchemy/ext/instrumentation.py +450 -0
  125. sqlalchemy/ext/mutable.py +1081 -0
  126. sqlalchemy/ext/orderinglist.py +440 -0
  127. sqlalchemy/ext/serializer.py +184 -0
  128. sqlalchemy/future/__init__.py +17 -0
  129. sqlalchemy/future/engine.py +15 -0
  130. sqlalchemy/inspection.py +188 -0
  131. sqlalchemy/log.py +279 -0
  132. sqlalchemy/orm/__init__.py +176 -0
  133. sqlalchemy/orm/_orm_constructors.py +2694 -0
  134. sqlalchemy/orm/_typing.py +180 -0
  135. sqlalchemy/orm/attributes.py +2868 -0
  136. sqlalchemy/orm/base.py +991 -0
  137. sqlalchemy/orm/bulk_persistence.py +2168 -0
  138. sqlalchemy/orm/clsregistry.py +630 -0
  139. sqlalchemy/orm/collections.py +1569 -0
  140. sqlalchemy/orm/context.py +3475 -0
  141. sqlalchemy/orm/decl_api.py +2283 -0
  142. sqlalchemy/orm/decl_base.py +2320 -0
  143. sqlalchemy/orm/dependency.py +1306 -0
  144. sqlalchemy/orm/descriptor_props.py +1183 -0
  145. sqlalchemy/orm/dynamic.py +306 -0
  146. sqlalchemy/orm/evaluator.py +378 -0
  147. sqlalchemy/orm/events.py +3387 -0
  148. sqlalchemy/orm/exc.py +237 -0
  149. sqlalchemy/orm/identity.py +302 -0
  150. sqlalchemy/orm/instrumentation.py +749 -0
  151. sqlalchemy/orm/interfaces.py +1595 -0
  152. sqlalchemy/orm/loading.py +1712 -0
  153. sqlalchemy/orm/mapped_collection.py +557 -0
  154. sqlalchemy/orm/mapper.py +4465 -0
  155. sqlalchemy/orm/path_registry.py +907 -0
  156. sqlalchemy/orm/persistence.py +1790 -0
  157. sqlalchemy/orm/properties.py +972 -0
  158. sqlalchemy/orm/query.py +3528 -0
  159. sqlalchemy/orm/relationships.py +3608 -0
  160. sqlalchemy/orm/scoping.py +2233 -0
  161. sqlalchemy/orm/session.py +5468 -0
  162. sqlalchemy/orm/state.py +1175 -0
  163. sqlalchemy/orm/state_changes.py +196 -0
  164. sqlalchemy/orm/strategies.py +3552 -0
  165. sqlalchemy/orm/strategy_options.py +2648 -0
  166. sqlalchemy/orm/sync.py +164 -0
  167. sqlalchemy/orm/unitofwork.py +797 -0
  168. sqlalchemy/orm/util.py +2461 -0
  169. sqlalchemy/orm/writeonly.py +701 -0
  170. sqlalchemy/pool/__init__.py +41 -0
  171. sqlalchemy/pool/base.py +1540 -0
  172. sqlalchemy/pool/events.py +375 -0
  173. sqlalchemy/pool/impl.py +583 -0
  174. sqlalchemy/py.typed +0 -0
  175. sqlalchemy/schema.py +75 -0
  176. sqlalchemy/sql/__init__.py +156 -0
  177. sqlalchemy/sql/_annotated_cols.py +402 -0
  178. sqlalchemy/sql/_cache_key_cy.cp315-win32.pyd +0 -0
  179. sqlalchemy/sql/_cache_key_cy.py +363 -0
  180. sqlalchemy/sql/_dml_constructors.py +132 -0
  181. sqlalchemy/sql/_elements_constructors.py +2190 -0
  182. sqlalchemy/sql/_orm_types.py +19 -0
  183. sqlalchemy/sql/_selectable_constructors.py +840 -0
  184. sqlalchemy/sql/_typing.py +500 -0
  185. sqlalchemy/sql/_util_cy.cp315-win32.pyd +0 -0
  186. sqlalchemy/sql/_util_cy.pxd +11 -0
  187. sqlalchemy/sql/_util_cy.py +127 -0
  188. sqlalchemy/sql/annotation.py +590 -0
  189. sqlalchemy/sql/base.py +2702 -0
  190. sqlalchemy/sql/cache_key.py +915 -0
  191. sqlalchemy/sql/coercions.py +1373 -0
  192. sqlalchemy/sql/compiler.py +8453 -0
  193. sqlalchemy/sql/crud.py +1816 -0
  194. sqlalchemy/sql/ddl.py +1962 -0
  195. sqlalchemy/sql/default_comparator.py +660 -0
  196. sqlalchemy/sql/dml.py +2018 -0
  197. sqlalchemy/sql/elements.py +6057 -0
  198. sqlalchemy/sql/events.py +458 -0
  199. sqlalchemy/sql/expression.py +171 -0
  200. sqlalchemy/sql/functions.py +2380 -0
  201. sqlalchemy/sql/lambdas.py +1442 -0
  202. sqlalchemy/sql/naming.py +204 -0
  203. sqlalchemy/sql/operators.py +2909 -0
  204. sqlalchemy/sql/roles.py +332 -0
  205. sqlalchemy/sql/schema.py +7075 -0
  206. sqlalchemy/sql/selectable.py +7634 -0
  207. sqlalchemy/sql/sqltypes.py +4130 -0
  208. sqlalchemy/sql/traversals.py +1041 -0
  209. sqlalchemy/sql/type_api.py +2450 -0
  210. sqlalchemy/sql/util.py +1496 -0
  211. sqlalchemy/sql/visitors.py +1153 -0
  212. sqlalchemy/testing/__init__.py +97 -0
  213. sqlalchemy/testing/assertions.py +1007 -0
  214. sqlalchemy/testing/assertsql.py +519 -0
  215. sqlalchemy/testing/asyncio.py +128 -0
  216. sqlalchemy/testing/cancellation.py +237 -0
  217. sqlalchemy/testing/config.py +440 -0
  218. sqlalchemy/testing/engines.py +482 -0
  219. sqlalchemy/testing/entities.py +117 -0
  220. sqlalchemy/testing/exclusions.py +501 -0
  221. sqlalchemy/testing/fixtures/__init__.py +30 -0
  222. sqlalchemy/testing/fixtures/base.py +426 -0
  223. sqlalchemy/testing/fixtures/mypy.py +247 -0
  224. sqlalchemy/testing/fixtures/orm.py +227 -0
  225. sqlalchemy/testing/fixtures/sql.py +538 -0
  226. sqlalchemy/testing/pickleable.py +155 -0
  227. sqlalchemy/testing/plugin/__init__.py +6 -0
  228. sqlalchemy/testing/plugin/bootstrap.py +50 -0
  229. sqlalchemy/testing/plugin/plugin_base.py +828 -0
  230. sqlalchemy/testing/plugin/pytestplugin.py +896 -0
  231. sqlalchemy/testing/profiles_file.py +350 -0
  232. sqlalchemy/testing/profiling.py +294 -0
  233. sqlalchemy/testing/provision.py +633 -0
  234. sqlalchemy/testing/requirements.py +1971 -0
  235. sqlalchemy/testing/schema.py +198 -0
  236. sqlalchemy/testing/suite/__init__.py +19 -0
  237. sqlalchemy/testing/suite/test_cte.py +237 -0
  238. sqlalchemy/testing/suite/test_ddl.py +420 -0
  239. sqlalchemy/testing/suite/test_dialect.py +776 -0
  240. sqlalchemy/testing/suite/test_insert.py +630 -0
  241. sqlalchemy/testing/suite/test_reflection.py +3815 -0
  242. sqlalchemy/testing/suite/test_results.py +660 -0
  243. sqlalchemy/testing/suite/test_rowcount.py +258 -0
  244. sqlalchemy/testing/suite/test_select.py +2112 -0
  245. sqlalchemy/testing/suite/test_sequence.py +317 -0
  246. sqlalchemy/testing/suite/test_table_via_select.py +686 -0
  247. sqlalchemy/testing/suite/test_types.py +2271 -0
  248. sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
  249. sqlalchemy/testing/suite/test_update_delete.py +139 -0
  250. sqlalchemy/testing/util.py +575 -0
  251. sqlalchemy/testing/warnings.py +52 -0
  252. sqlalchemy/types.py +75 -0
  253. sqlalchemy/util/__init__.py +165 -0
  254. sqlalchemy/util/_collections.py +688 -0
  255. sqlalchemy/util/_collections_cy.cp315-win32.pyd +0 -0
  256. sqlalchemy/util/_collections_cy.pxd +8 -0
  257. sqlalchemy/util/_collections_cy.py +516 -0
  258. sqlalchemy/util/_has_cython.py +48 -0
  259. sqlalchemy/util/_immutabledict_cy.cp315-win32.pyd +0 -0
  260. sqlalchemy/util/_immutabledict_cy.py +240 -0
  261. sqlalchemy/util/compat.py +298 -0
  262. sqlalchemy/util/concurrency.py +272 -0
  263. sqlalchemy/util/cython.py +95 -0
  264. sqlalchemy/util/deprecations.py +401 -0
  265. sqlalchemy/util/langhelpers.py +2797 -0
  266. sqlalchemy/util/preloaded.py +153 -0
  267. sqlalchemy/util/queue.py +304 -0
  268. sqlalchemy/util/tool_support.py +202 -0
  269. sqlalchemy/util/topological.py +120 -0
  270. sqlalchemy/util/typing.py +709 -0
  271. sqlalchemy-2.1.0rc1.dist-info/METADATA +270 -0
  272. sqlalchemy-2.1.0rc1.dist-info/RECORD +276 -0
  273. sqlalchemy-2.1.0rc1.dist-info/WHEEL +5 -0
  274. sqlalchemy-2.1.0rc1.dist-info/licenses/AUTHORS +30 -0
  275. sqlalchemy-2.1.0rc1.dist-info/licenses/LICENSE +19 -0
  276. sqlalchemy-2.1.0rc1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,561 @@
1
+ # ext/declarative/extensions.py
2
+ # Copyright (C) 2005-2026 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
+ # mypy: ignore-errors
8
+
9
+
10
+ """Public API functions and helpers for declarative."""
11
+
12
+ from __future__ import annotations
13
+
14
+ import collections
15
+ import contextlib
16
+ from typing import Any
17
+ from typing import Callable
18
+ from typing import TYPE_CHECKING
19
+ from typing import Union
20
+
21
+ from ... import exc as sa_exc
22
+ from ...engine import Connection
23
+ from ...engine import Engine
24
+ from ...orm import exc as orm_exc
25
+ from ...orm import relationships
26
+ from ...orm.base import _mapper_or_none
27
+ from ...orm.clsregistry import _resolver
28
+ from ...orm.decl_base import _DeferredDeclarativeConfig
29
+ from ...orm.util import polymorphic_union
30
+ from ...schema import Table
31
+ from ...util import OrderedDict
32
+
33
+ if TYPE_CHECKING:
34
+ from ...sql.schema import MetaData
35
+
36
+
37
+ class ConcreteBase:
38
+ """A helper class for 'concrete' declarative mappings.
39
+
40
+ :class:`.ConcreteBase` will use the :func:`.polymorphic_union`
41
+ function automatically, against all tables mapped as a subclass
42
+ to this class. The function is called via the
43
+ ``__declare_last__()`` function, which is essentially
44
+ a hook for the :meth:`.MapperEvents.after_configured` event.
45
+
46
+ :class:`.ConcreteBase` produces a mapped
47
+ table for the class itself. Compare to :class:`.AbstractConcreteBase`,
48
+ which does not.
49
+
50
+ Example::
51
+
52
+ from sqlalchemy.ext.declarative import ConcreteBase
53
+
54
+
55
+ class Employee(ConcreteBase, Base):
56
+ __tablename__ = "employee"
57
+ employee_id = Column(Integer, primary_key=True)
58
+ name = Column(String(50))
59
+ __mapper_args__ = {
60
+ "polymorphic_identity": "employee",
61
+ "concrete": True,
62
+ }
63
+
64
+
65
+ class Manager(Employee):
66
+ __tablename__ = "manager"
67
+ employee_id = Column(Integer, primary_key=True)
68
+ name = Column(String(50))
69
+ manager_data = Column(String(40))
70
+ __mapper_args__ = {
71
+ "polymorphic_identity": "manager",
72
+ "concrete": True,
73
+ }
74
+
75
+ The name of the discriminator column used by :func:`.polymorphic_union`
76
+ defaults to the name ``type``. To suit the use case of a mapping where an
77
+ actual column in a mapped table is already named ``type``, the
78
+ discriminator name can be configured by setting the
79
+ ``_concrete_discriminator_name`` attribute::
80
+
81
+ class Employee(ConcreteBase, Base):
82
+ _concrete_discriminator_name = "_concrete_discriminator"
83
+
84
+ .. versionchanged:: 1.4.2 The ``_concrete_discriminator_name`` attribute
85
+ need only be placed on the basemost class to take correct effect for
86
+ all subclasses. An explicit error message is now raised if the
87
+ mapped column names conflict with the discriminator name, whereas
88
+ in the 1.3.x series there would be some warnings and then a non-useful
89
+ query would be generated.
90
+
91
+ .. seealso::
92
+
93
+ :class:`.AbstractConcreteBase`
94
+
95
+ :ref:`concrete_inheritance`
96
+
97
+
98
+ """
99
+
100
+ @classmethod
101
+ def _create_polymorphic_union(cls, mappers, discriminator_name):
102
+ return polymorphic_union(
103
+ OrderedDict(
104
+ (mp.polymorphic_identity, mp.local_table) for mp in mappers
105
+ ),
106
+ discriminator_name,
107
+ "pjoin",
108
+ )
109
+
110
+ @classmethod
111
+ def __declare_first__(cls):
112
+ m = cls.__mapper__
113
+ if m.with_polymorphic:
114
+ return
115
+
116
+ discriminator_name = (
117
+ getattr(cls, "_concrete_discriminator_name", None) or "type"
118
+ )
119
+
120
+ mappers = list(m.self_and_descendants)
121
+ pjoin = cls._create_polymorphic_union(mappers, discriminator_name)
122
+ m._set_with_polymorphic(("*", pjoin))
123
+ m._set_polymorphic_on(pjoin.c[discriminator_name])
124
+
125
+
126
+ class AbstractConcreteBase(ConcreteBase):
127
+ """A helper class for 'concrete' declarative mappings.
128
+
129
+ :class:`.AbstractConcreteBase` will use the :func:`.polymorphic_union`
130
+ function automatically, against all tables mapped as a subclass
131
+ to this class. The function is called via the
132
+ ``__declare_first__()`` function, which is essentially
133
+ a hook for the :meth:`.MapperEvents.before_configured` event.
134
+
135
+ :class:`.AbstractConcreteBase` applies :class:`_orm.Mapper` for its
136
+ immediately inheriting class, as would occur for any other
137
+ declarative mapped class. However, the :class:`_orm.Mapper` is not
138
+ mapped to any particular :class:`.Table` object. Instead, it's
139
+ mapped directly to the "polymorphic" selectable produced by
140
+ :func:`.polymorphic_union`, and performs no persistence operations on its
141
+ own. Compare to :class:`.ConcreteBase`, which maps its
142
+ immediately inheriting class to an actual
143
+ :class:`.Table` that stores rows directly.
144
+
145
+ .. note::
146
+
147
+ The :class:`.AbstractConcreteBase` delays the mapper creation of the
148
+ base class until all the subclasses have been defined,
149
+ as it needs to create a mapping against a selectable that will include
150
+ all subclass tables. In order to achieve this, it waits for the
151
+ **mapper configuration event** to occur, at which point it scans
152
+ through all the configured subclasses and sets up a mapping that will
153
+ query against all subclasses at once.
154
+
155
+ While this event is normally invoked automatically, in the case of
156
+ :class:`.AbstractConcreteBase`, it may be necessary to invoke it
157
+ explicitly after **all** subclass mappings are defined, if the first
158
+ operation is to be a query against this base class. To do so, once all
159
+ the desired classes have been configured, the
160
+ :meth:`_orm.registry.configure` method on the :class:`_orm.registry`
161
+ in use can be invoked, which is available in relation to a particular
162
+ declarative base class::
163
+
164
+ Base.registry.configure()
165
+
166
+ Example::
167
+
168
+ from sqlalchemy.orm import DeclarativeBase
169
+ from sqlalchemy.ext.declarative import AbstractConcreteBase
170
+
171
+
172
+ class Base(DeclarativeBase):
173
+ pass
174
+
175
+
176
+ class Employee(AbstractConcreteBase, Base):
177
+ pass
178
+
179
+
180
+ class Manager(Employee):
181
+ __tablename__ = "manager"
182
+ employee_id = Column(Integer, primary_key=True)
183
+ name = Column(String(50))
184
+ manager_data = Column(String(40))
185
+
186
+ __mapper_args__ = {
187
+ "polymorphic_identity": "manager",
188
+ "concrete": True,
189
+ }
190
+
191
+
192
+ Base.registry.configure()
193
+
194
+ The abstract base class is handled by declarative in a special way;
195
+ at class configuration time, it behaves like a declarative mixin
196
+ or an ``__abstract__`` base class. Once classes are configured
197
+ and mappings are produced, it then gets mapped itself, but
198
+ after all of its descendants. This is a very unique system of mapping
199
+ not found in any other SQLAlchemy API feature.
200
+
201
+ Using this approach, we can specify columns and properties
202
+ that will take place on mapped subclasses, in the way that
203
+ we normally do as in :ref:`declarative_mixins`::
204
+
205
+ from sqlalchemy.ext.declarative import AbstractConcreteBase
206
+
207
+
208
+ class Company(Base):
209
+ __tablename__ = "company"
210
+ id = Column(Integer, primary_key=True)
211
+
212
+
213
+ class Employee(AbstractConcreteBase, Base):
214
+ strict_attrs = True
215
+
216
+ employee_id = Column(Integer, primary_key=True)
217
+
218
+ @declared_attr
219
+ def company_id(cls):
220
+ return Column(ForeignKey("company.id"))
221
+
222
+ @declared_attr
223
+ def company(cls):
224
+ return relationship("Company")
225
+
226
+
227
+ class Manager(Employee):
228
+ __tablename__ = "manager"
229
+
230
+ name = Column(String(50))
231
+ manager_data = Column(String(40))
232
+
233
+ __mapper_args__ = {
234
+ "polymorphic_identity": "manager",
235
+ "concrete": True,
236
+ }
237
+
238
+
239
+ Base.registry.configure()
240
+
241
+ When we make use of our mappings however, both ``Manager`` and
242
+ ``Employee`` will have an independently usable ``.company`` attribute::
243
+
244
+ session.execute(select(Employee).filter(Employee.company.has(id=5)))
245
+
246
+ :param strict_attrs: when specified on the base class, "strict" attribute
247
+ mode is enabled which attempts to limit ORM mapped attributes on the
248
+ base class to only those that are immediately present, while still
249
+ preserving "polymorphic" loading behavior.
250
+
251
+ .. versionadded:: 2.0
252
+
253
+ .. seealso::
254
+
255
+ :class:`.ConcreteBase`
256
+
257
+ :ref:`concrete_inheritance`
258
+
259
+ :ref:`abstract_concrete_base`
260
+
261
+ """
262
+
263
+ __no_table__ = True
264
+
265
+ @classmethod
266
+ def __declare_first__(cls):
267
+ cls._sa_decl_prepare_nocascade()
268
+
269
+ @classmethod
270
+ def _sa_decl_prepare_nocascade(cls):
271
+ if getattr(cls, "__mapper__", None):
272
+ return
273
+
274
+ to_map = _DeferredDeclarativeConfig.config_for_cls(cls)
275
+
276
+ # can't rely on 'self_and_descendants' here
277
+ # since technically an immediate subclass
278
+ # might not be mapped, but a subclass
279
+ # may be.
280
+ mappers = []
281
+ stack = list(cls.__subclasses__())
282
+ while stack:
283
+ klass = stack.pop()
284
+ stack.extend(klass.__subclasses__())
285
+ mn = _mapper_or_none(klass)
286
+ if mn is not None:
287
+ mappers.append(mn)
288
+
289
+ discriminator_name = (
290
+ getattr(cls, "_concrete_discriminator_name", None) or "type"
291
+ )
292
+ pjoin = cls._create_polymorphic_union(mappers, discriminator_name)
293
+
294
+ # For columns that were declared on the class, these
295
+ # are normally ignored with the "__no_table__" mapping,
296
+ # unless they have a different attribute key vs. col name
297
+ # and are in the properties argument.
298
+ # In that case, ensure we update the properties entry
299
+ # to the correct column from the pjoin target table.
300
+ declared_cols = set(to_map.declared_columns)
301
+ declared_col_keys = {c.key for c in declared_cols}
302
+ for k, v in list(to_map.properties.items()):
303
+ if v in declared_cols:
304
+ to_map.properties[k] = pjoin.c[v.key]
305
+ declared_col_keys.remove(v.key)
306
+
307
+ to_map.local_table = pjoin
308
+
309
+ strict_attrs = cls.__dict__.get("strict_attrs", False)
310
+
311
+ m_args = to_map.mapper_args_fn or dict
312
+
313
+ def mapper_args():
314
+ args = m_args()
315
+ args["polymorphic_on"] = pjoin.c[discriminator_name]
316
+ args["polymorphic_abstract"] = True
317
+ if strict_attrs:
318
+ args["include_properties"] = (
319
+ set(pjoin.primary_key)
320
+ | declared_col_keys
321
+ | {discriminator_name}
322
+ )
323
+ args["with_polymorphic"] = ("*", pjoin)
324
+ return args
325
+
326
+ to_map.mapper_args_fn = mapper_args
327
+
328
+ to_map.map()
329
+
330
+ stack = [cls]
331
+ while stack:
332
+ scls = stack.pop(0)
333
+ stack.extend(scls.__subclasses__())
334
+ sm = _mapper_or_none(scls)
335
+ if sm and sm.concrete and sm.inherits is None:
336
+ for sup_ in scls.__mro__[1:]:
337
+ sup_sm = _mapper_or_none(sup_)
338
+ if sup_sm:
339
+ sm._set_concrete_base(sup_sm)
340
+ break
341
+
342
+ @classmethod
343
+ def _sa_raise_deferred_config(cls):
344
+ raise orm_exc.UnmappedClassError(
345
+ cls,
346
+ msg="Class %s is a subclass of AbstractConcreteBase and "
347
+ "has a mapping pending until all subclasses are defined. "
348
+ "Call the sqlalchemy.orm.configure_mappers() function after "
349
+ "all subclasses have been defined to "
350
+ "complete the mapping of this class."
351
+ % orm_exc._safe_cls_name(cls),
352
+ )
353
+
354
+
355
+ class DeferredReflection:
356
+ """A helper class for construction of mappings based on
357
+ a deferred reflection step.
358
+
359
+ Normally, declarative can be used with reflection by
360
+ setting a :class:`_schema.Table` object using autoload_with=engine
361
+ as the ``__table__`` attribute on a declarative class.
362
+ The caveat is that the :class:`_schema.Table` must be fully
363
+ reflected, or at the very least have a primary key column,
364
+ at the point at which a normal declarative mapping is
365
+ constructed, meaning the :class:`_engine.Engine` must be available
366
+ at class declaration time.
367
+
368
+ The :class:`.DeferredReflection` mixin moves the construction
369
+ of mappers to be at a later point, after a specific
370
+ method is called which first reflects all :class:`_schema.Table`
371
+ objects created so far. Classes can define it as such::
372
+
373
+ from sqlalchemy.ext.declarative import declarative_base
374
+ from sqlalchemy.ext.declarative import DeferredReflection
375
+
376
+ Base = declarative_base()
377
+
378
+
379
+ class MyClass(DeferredReflection, Base):
380
+ __tablename__ = "mytable"
381
+
382
+ Above, ``MyClass`` is not yet mapped. After a series of
383
+ classes have been defined in the above fashion, all tables
384
+ can be reflected and mappings created using
385
+ :meth:`.prepare`::
386
+
387
+ engine = create_engine("someengine://...")
388
+ DeferredReflection.prepare(engine)
389
+
390
+ The :class:`.DeferredReflection` mixin can be applied to individual
391
+ classes, used as the base for the declarative base itself,
392
+ or used in a custom abstract class. Using an abstract base
393
+ allows that only a subset of classes to be prepared for a
394
+ particular prepare step, which is necessary for applications
395
+ that use more than one engine. For example, if an application
396
+ has two engines, you might use two bases, and prepare each
397
+ separately, e.g.::
398
+
399
+ class ReflectedOne(DeferredReflection, Base):
400
+ __abstract__ = True
401
+
402
+
403
+ class ReflectedTwo(DeferredReflection, Base):
404
+ __abstract__ = True
405
+
406
+
407
+ class MyClass(ReflectedOne):
408
+ __tablename__ = "mytable"
409
+
410
+
411
+ class MyOtherClass(ReflectedOne):
412
+ __tablename__ = "myothertable"
413
+
414
+
415
+ class YetAnotherClass(ReflectedTwo):
416
+ __tablename__ = "yetanothertable"
417
+
418
+
419
+ # ... etc.
420
+
421
+ Above, the class hierarchies for ``ReflectedOne`` and
422
+ ``ReflectedTwo`` can be configured separately::
423
+
424
+ ReflectedOne.prepare(engine_one)
425
+ ReflectedTwo.prepare(engine_two)
426
+
427
+ .. seealso::
428
+
429
+ :ref:`orm_declarative_reflected_deferred_reflection` - in the
430
+ :ref:`orm_declarative_table_config_toplevel` section.
431
+
432
+ """
433
+
434
+ @classmethod
435
+ def prepare(
436
+ cls, bind: Union[Engine, Connection], **reflect_kw: Any
437
+ ) -> None:
438
+ r"""Reflect all :class:`_schema.Table` objects for all current
439
+ :class:`.DeferredReflection` subclasses
440
+
441
+ :param bind: :class:`_engine.Engine` or :class:`_engine.Connection`
442
+ instance
443
+
444
+ ..versionchanged:: 2.0.16 a :class:`_engine.Connection` is also
445
+ accepted.
446
+
447
+ :param \**reflect_kw: additional keyword arguments passed to
448
+ :meth:`_schema.MetaData.reflect`, such as
449
+ :paramref:`_schema.MetaData.reflect.views`.
450
+
451
+ .. versionadded:: 2.0.16
452
+
453
+ """
454
+
455
+ to_map = _DeferredDeclarativeConfig.classes_for_base(cls)
456
+
457
+ metadata_to_table = collections.defaultdict(set)
458
+
459
+ # first collect the primary __table__ for each class into a
460
+ # collection of metadata/schemaname -> table names
461
+ for thingy in to_map:
462
+ if thingy.local_table is not None:
463
+ metadata_to_table[
464
+ (thingy.local_table.metadata, thingy.local_table.schema)
465
+ ].add(thingy.local_table.name)
466
+
467
+ # then reflect all those tables into their metadatas
468
+
469
+ if isinstance(bind, Connection):
470
+ conn = bind
471
+ ctx = contextlib.nullcontext(enter_result=conn)
472
+ elif isinstance(bind, Engine):
473
+ ctx = bind.connect()
474
+ else:
475
+ raise sa_exc.ArgumentError(
476
+ f"Expected Engine or Connection, got {bind!r}"
477
+ )
478
+
479
+ with ctx as conn:
480
+ for (metadata, schema), table_names in metadata_to_table.items():
481
+ metadata.reflect(
482
+ conn,
483
+ only=table_names,
484
+ schema=schema,
485
+ extend_existing=True,
486
+ autoload_replace=False,
487
+ **reflect_kw,
488
+ )
489
+
490
+ metadata_to_table.clear()
491
+
492
+ # .map() each class, then go through relationships and look
493
+ # for secondary
494
+ for thingy in to_map:
495
+ thingy.map()
496
+
497
+ mapper = thingy.cls.__mapper__
498
+ metadata = mapper.class_.metadata
499
+
500
+ for rel in mapper._props.values():
501
+ if (
502
+ isinstance(rel, relationships.RelationshipProperty)
503
+ and rel._init_args.secondary._is_populated()
504
+ ):
505
+ secondary_arg = rel._init_args.secondary
506
+
507
+ if isinstance(secondary_arg.argument, Table):
508
+ secondary_table = secondary_arg.argument
509
+ metadata_to_table[
510
+ (
511
+ secondary_table.metadata,
512
+ secondary_table.schema,
513
+ )
514
+ ].add(secondary_table.name)
515
+ elif isinstance(secondary_arg.argument, str):
516
+ _, resolve_arg = _resolver(rel.parent.class_, rel)
517
+
518
+ resolver = resolve_arg(
519
+ secondary_arg.argument, True
520
+ )
521
+ metadata_to_table[
522
+ (metadata, thingy.local_table.schema)
523
+ ].add(secondary_arg.argument)
524
+
525
+ resolver._resolvers += (
526
+ cls._sa_deferred_table_resolver(metadata),
527
+ )
528
+
529
+ secondary_arg.argument = resolver()
530
+
531
+ for (metadata, schema), table_names in metadata_to_table.items():
532
+ metadata.reflect(
533
+ conn,
534
+ only=table_names,
535
+ schema=schema,
536
+ extend_existing=True,
537
+ autoload_replace=False,
538
+ )
539
+
540
+ @classmethod
541
+ def _sa_deferred_table_resolver(
542
+ cls, metadata: MetaData
543
+ ) -> Callable[[str], Table]:
544
+ def _resolve(key: str) -> Table:
545
+ # reflection has already occurred so this Table would have
546
+ # its contents already
547
+ return Table(key, metadata)
548
+
549
+ return _resolve
550
+
551
+ _sa_decl_prepare = True
552
+
553
+ @classmethod
554
+ def _sa_raise_deferred_config(cls):
555
+ raise orm_exc.UnmappedClassError(
556
+ cls,
557
+ msg="Class %s is a subclass of DeferredReflection. "
558
+ "Mappings are not produced until the .prepare() "
559
+ "method is called on the class hierarchy."
560
+ % orm_exc._safe_cls_name(cls),
561
+ )