automap.py 60 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701
  1. # ext/automap.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. r"""Define an extension to the :mod:`sqlalchemy.ext.declarative` system
  8. which automatically generates mapped classes and relationships from a database
  9. schema, typically though not necessarily one which is reflected.
  10. It is hoped that the :class:`.AutomapBase` system provides a quick
  11. and modernized solution to the problem that the very famous
  12. `SQLSoup <https://pypi.org/project/sqlsoup/>`_
  13. also tries to solve, that of generating a quick and rudimentary object
  14. model from an existing database on the fly. By addressing the issue strictly
  15. at the mapper configuration level, and integrating fully with existing
  16. Declarative class techniques, :class:`.AutomapBase` seeks to provide
  17. a well-integrated approach to the issue of expediently auto-generating ad-hoc
  18. mappings.
  19. .. tip:: The :ref:`automap_toplevel` extension is geared towards a
  20. "zero declaration" approach, where a complete ORM model including classes
  21. and pre-named relationships can be generated on the fly from a database
  22. schema. For applications that still want to use explicit class declarations
  23. including explicit relationship definitions in conjunction with reflection
  24. of tables, the :class:`.DeferredReflection` class, described at
  25. :ref:`orm_declarative_reflected_deferred_reflection`, is a better choice.
  26. .. _automap_basic_use:
  27. Basic Use
  28. =========
  29. The simplest usage is to reflect an existing database into a new model.
  30. We create a new :class:`.AutomapBase` class in a similar manner as to how
  31. we create a declarative base class, using :func:`.automap_base`.
  32. We then call :meth:`.AutomapBase.prepare` on the resulting base class,
  33. asking it to reflect the schema and produce mappings::
  34. from sqlalchemy.ext.automap import automap_base
  35. from sqlalchemy.orm import Session
  36. from sqlalchemy import create_engine
  37. Base = automap_base()
  38. # engine, suppose it has two tables 'user' and 'address' set up
  39. engine = create_engine("sqlite:///mydatabase.db")
  40. # reflect the tables
  41. Base.prepare(autoload_with=engine)
  42. # mapped classes are now created with names by default
  43. # matching that of the table name.
  44. User = Base.classes.user
  45. Address = Base.classes.address
  46. session = Session(engine)
  47. # rudimentary relationships are produced
  48. session.add(Address(email_address="foo@bar.com", user=User(name="foo")))
  49. session.commit()
  50. # collection-based relationships are by default named
  51. # "<classname>_collection"
  52. u1 = session.query(User).first()
  53. print(u1.address_collection)
  54. Above, calling :meth:`.AutomapBase.prepare` while passing along the
  55. :paramref:`.AutomapBase.prepare.reflect` parameter indicates that the
  56. :meth:`_schema.MetaData.reflect`
  57. method will be called on this declarative base
  58. classes' :class:`_schema.MetaData` collection; then, each **viable**
  59. :class:`_schema.Table` within the :class:`_schema.MetaData`
  60. will get a new mapped class
  61. generated automatically. The :class:`_schema.ForeignKeyConstraint`
  62. objects which
  63. link the various tables together will be used to produce new, bidirectional
  64. :func:`_orm.relationship` objects between classes.
  65. The classes and relationships
  66. follow along a default naming scheme that we can customize. At this point,
  67. our basic mapping consisting of related ``User`` and ``Address`` classes is
  68. ready to use in the traditional way.
  69. .. note:: By **viable**, we mean that for a table to be mapped, it must
  70. specify a primary key. Additionally, if the table is detected as being
  71. a pure association table between two other tables, it will not be directly
  72. mapped and will instead be configured as a many-to-many table between
  73. the mappings for the two referring tables.
  74. Generating Mappings from an Existing MetaData
  75. =============================================
  76. We can pass a pre-declared :class:`_schema.MetaData` object to
  77. :func:`.automap_base`.
  78. This object can be constructed in any way, including programmatically, from
  79. a serialized file, or from itself being reflected using
  80. :meth:`_schema.MetaData.reflect`.
  81. Below we illustrate a combination of reflection and
  82. explicit table declaration::
  83. from sqlalchemy import create_engine, MetaData, Table, Column, ForeignKey
  84. from sqlalchemy.ext.automap import automap_base
  85. engine = create_engine("sqlite:///mydatabase.db")
  86. # produce our own MetaData object
  87. metadata = MetaData()
  88. # we can reflect it ourselves from a database, using options
  89. # such as 'only' to limit what tables we look at...
  90. metadata.reflect(engine, only=["user", "address"])
  91. # ... or just define our own Table objects with it (or combine both)
  92. Table(
  93. "user_order",
  94. metadata,
  95. Column("id", Integer, primary_key=True),
  96. Column("user_id", ForeignKey("user.id")),
  97. )
  98. # we can then produce a set of mappings from this MetaData.
  99. Base = automap_base(metadata=metadata)
  100. # calling prepare() just sets up mapped classes and relationships.
  101. Base.prepare()
  102. # mapped classes are ready
  103. User = Base.classes.user
  104. Address = Base.classes.address
  105. Order = Base.classes.user_order
  106. .. _automap_by_module:
  107. Generating Mappings from Multiple Schemas
  108. =========================================
  109. The :meth:`.AutomapBase.prepare` method when used with reflection may reflect
  110. tables from one schema at a time at most, using the
  111. :paramref:`.AutomapBase.prepare.schema` parameter to indicate the name of a
  112. schema to be reflected from. In order to populate the :class:`.AutomapBase`
  113. with tables from multiple schemas, :meth:`.AutomapBase.prepare` may be invoked
  114. multiple times, each time passing a different name to the
  115. :paramref:`.AutomapBase.prepare.schema` parameter. The
  116. :meth:`.AutomapBase.prepare` method keeps an internal list of
  117. :class:`_schema.Table` objects that have already been mapped, and will add new
  118. mappings only for those :class:`_schema.Table` objects that are new since the
  119. last time :meth:`.AutomapBase.prepare` was run::
  120. e = create_engine("postgresql://scott:tiger@localhost/test")
  121. Base.metadata.create_all(e)
  122. Base = automap_base()
  123. Base.prepare(e)
  124. Base.prepare(e, schema="test_schema")
  125. Base.prepare(e, schema="test_schema_2")
  126. .. versionadded:: 2.0 The :meth:`.AutomapBase.prepare` method may be called
  127. any number of times; only newly added tables will be mapped
  128. on each run. Previously in version 1.4 and earlier, multiple calls would
  129. cause errors as it would attempt to re-map an already mapped class.
  130. The previous workaround approach of invoking
  131. :meth:`_schema.MetaData.reflect` directly remains available as well.
  132. Automapping same-named tables across multiple schemas
  133. -----------------------------------------------------
  134. For the common case where multiple schemas may have same-named tables and
  135. therefore would generate same-named classes, conflicts can be resolved either
  136. through use of the :paramref:`.AutomapBase.prepare.classname_for_table` hook to
  137. apply different classnames on a per-schema basis, or by using the
  138. :paramref:`.AutomapBase.prepare.modulename_for_table` hook, which allows
  139. disambiguation of same-named classes by changing their effective ``__module__``
  140. attribute. In the example below, this hook is used to create a ``__module__``
  141. attribute for all classes that is of the form ``mymodule.<schemaname>``, where
  142. the schema name ``default`` is used if no schema is present::
  143. e = create_engine("postgresql://scott:tiger@localhost/test")
  144. Base.metadata.create_all(e)
  145. def module_name_for_table(cls, tablename, table):
  146. if table.schema is not None:
  147. return f"mymodule.{table.schema}"
  148. else:
  149. return f"mymodule.default"
  150. Base = automap_base()
  151. Base.prepare(e, modulename_for_table=module_name_for_table)
  152. Base.prepare(
  153. e, schema="test_schema", modulename_for_table=module_name_for_table
  154. )
  155. Base.prepare(
  156. e, schema="test_schema_2", modulename_for_table=module_name_for_table
  157. )
  158. The same named-classes are organized into a hierarchical collection available
  159. at :attr:`.AutomapBase.by_module`. This collection is traversed using the
  160. dot-separated name of a particular package/module down into the desired
  161. class name.
  162. .. note:: When using the :paramref:`.AutomapBase.prepare.modulename_for_table`
  163. hook to return a new ``__module__`` that is not ``None``, the class is
  164. **not** placed into the :attr:`.AutomapBase.classes` collection; only
  165. classes that were not given an explicit modulename are placed here, as the
  166. collection cannot represent same-named classes individually.
  167. In the example above, if the database contained a table named ``accounts`` in
  168. all three of the default schema, the ``test_schema`` schema, and the
  169. ``test_schema_2`` schema, three separate classes will be available as::
  170. Base.by_module.mymodule.default.accounts
  171. Base.by_module.mymodule.test_schema.accounts
  172. Base.by_module.mymodule.test_schema_2.accounts
  173. The default module namespace generated for all :class:`.AutomapBase` classes is
  174. ``sqlalchemy.ext.automap``. If no
  175. :paramref:`.AutomapBase.prepare.modulename_for_table` hook is used, the
  176. contents of :attr:`.AutomapBase.by_module` will be entirely within the
  177. ``sqlalchemy.ext.automap`` namespace (e.g.
  178. ``MyBase.by_module.sqlalchemy.ext.automap.<classname>``), which would contain
  179. the same series of classes as what would be seen in
  180. :attr:`.AutomapBase.classes`. Therefore it's generally only necessary to use
  181. :attr:`.AutomapBase.by_module` when explicit ``__module__`` conventions are
  182. present.
  183. .. versionadded: 2.0
  184. Added the :attr:`.AutomapBase.by_module` collection, which stores
  185. classes within a named hierarchy based on dot-separated module names,
  186. as well as the :paramref:`.Automap.prepare.modulename_for_table` parameter
  187. which allows for custom ``__module__`` schemes for automapped
  188. classes.
  189. Specifying Classes Explicitly
  190. =============================
  191. .. tip:: If explicit classes are expected to be prominent in an application,
  192. consider using :class:`.DeferredReflection` instead.
  193. The :mod:`.sqlalchemy.ext.automap` extension allows classes to be defined
  194. explicitly, in a way similar to that of the :class:`.DeferredReflection` class.
  195. Classes that extend from :class:`.AutomapBase` act like regular declarative
  196. classes, but are not immediately mapped after their construction, and are
  197. instead mapped when we call :meth:`.AutomapBase.prepare`. The
  198. :meth:`.AutomapBase.prepare` method will make use of the classes we've
  199. established based on the table name we use. If our schema contains tables
  200. ``user`` and ``address``, we can define one or both of the classes to be used::
  201. from sqlalchemy.ext.automap import automap_base
  202. from sqlalchemy import create_engine
  203. # automap base
  204. Base = automap_base()
  205. # pre-declare User for the 'user' table
  206. class User(Base):
  207. __tablename__ = "user"
  208. # override schema elements like Columns
  209. user_name = Column("name", String)
  210. # override relationships too, if desired.
  211. # we must use the same name that automap would use for the
  212. # relationship, and also must refer to the class name that automap will
  213. # generate for "address"
  214. address_collection = relationship("address", collection_class=set)
  215. # reflect
  216. engine = create_engine("sqlite:///mydatabase.db")
  217. Base.prepare(autoload_with=engine)
  218. # we still have Address generated from the tablename "address",
  219. # but User is the same as Base.classes.User now
  220. Address = Base.classes.address
  221. u1 = session.query(User).first()
  222. print(u1.address_collection)
  223. # the backref is still there:
  224. a1 = session.query(Address).first()
  225. print(a1.user)
  226. Above, one of the more intricate details is that we illustrated overriding
  227. one of the :func:`_orm.relationship` objects that automap would have created.
  228. To do this, we needed to make sure the names match up with what automap
  229. would normally generate, in that the relationship name would be
  230. ``User.address_collection`` and the name of the class referred to, from
  231. automap's perspective, is called ``address``, even though we are referring to
  232. it as ``Address`` within our usage of this class.
  233. Overriding Naming Schemes
  234. =========================
  235. :mod:`.sqlalchemy.ext.automap` is tasked with producing mapped classes and
  236. relationship names based on a schema, which means it has decision points in how
  237. these names are determined. These three decision points are provided using
  238. functions which can be passed to the :meth:`.AutomapBase.prepare` method, and
  239. are known as :func:`.classname_for_table`,
  240. :func:`.name_for_scalar_relationship`,
  241. and :func:`.name_for_collection_relationship`. Any or all of these
  242. functions are provided as in the example below, where we use a "camel case"
  243. scheme for class names and a "pluralizer" for collection names using the
  244. `Inflect <https://pypi.org/project/inflect>`_ package::
  245. import re
  246. import inflect
  247. def camelize_classname(base, tablename, table):
  248. "Produce a 'camelized' class name, e.g."
  249. "'words_and_underscores' -> 'WordsAndUnderscores'"
  250. return str(
  251. tablename[0].upper()
  252. + re.sub(
  253. r"_([a-z])",
  254. lambda m: m.group(1).upper(),
  255. tablename[1:],
  256. )
  257. )
  258. _pluralizer = inflect.engine()
  259. def pluralize_collection(base, local_cls, referred_cls, constraint):
  260. "Produce an 'uncamelized', 'pluralized' class name, e.g."
  261. "'SomeTerm' -> 'some_terms'"
  262. referred_name = referred_cls.__name__
  263. uncamelized = re.sub(
  264. r"[A-Z]",
  265. lambda m: "_%s" % m.group(0).lower(),
  266. referred_name,
  267. )[1:]
  268. pluralized = _pluralizer.plural(uncamelized)
  269. return pluralized
  270. from sqlalchemy.ext.automap import automap_base
  271. Base = automap_base()
  272. engine = create_engine("sqlite:///mydatabase.db")
  273. Base.prepare(
  274. autoload_with=engine,
  275. classname_for_table=camelize_classname,
  276. name_for_collection_relationship=pluralize_collection,
  277. )
  278. From the above mapping, we would now have classes ``User`` and ``Address``,
  279. where the collection from ``User`` to ``Address`` is called
  280. ``User.addresses``::
  281. User, Address = Base.classes.User, Base.classes.Address
  282. u1 = User(addresses=[Address(email="foo@bar.com")])
  283. Relationship Detection
  284. ======================
  285. The vast majority of what automap accomplishes is the generation of
  286. :func:`_orm.relationship` structures based on foreign keys. The mechanism
  287. by which this works for many-to-one and one-to-many relationships is as
  288. follows:
  289. 1. A given :class:`_schema.Table`, known to be mapped to a particular class,
  290. is examined for :class:`_schema.ForeignKeyConstraint` objects.
  291. 2. From each :class:`_schema.ForeignKeyConstraint`, the remote
  292. :class:`_schema.Table`
  293. object present is matched up to the class to which it is to be mapped,
  294. if any, else it is skipped.
  295. 3. As the :class:`_schema.ForeignKeyConstraint`
  296. we are examining corresponds to a
  297. reference from the immediate mapped class, the relationship will be set up
  298. as a many-to-one referring to the referred class; a corresponding
  299. one-to-many backref will be created on the referred class referring
  300. to this class.
  301. 4. If any of the columns that are part of the
  302. :class:`_schema.ForeignKeyConstraint`
  303. are not nullable (e.g. ``nullable=False``), a
  304. :paramref:`_orm.relationship.cascade` keyword argument
  305. of ``all, delete-orphan`` will be added to the keyword arguments to
  306. be passed to the relationship or backref. If the
  307. :class:`_schema.ForeignKeyConstraint` reports that
  308. :paramref:`_schema.ForeignKeyConstraint.ondelete`
  309. is set to ``CASCADE`` for a not null or ``SET NULL`` for a nullable
  310. set of columns, the option :paramref:`_orm.relationship.passive_deletes`
  311. flag is set to ``True`` in the set of relationship keyword arguments.
  312. Note that not all backends support reflection of ON DELETE.
  313. 5. The names of the relationships are determined using the
  314. :paramref:`.AutomapBase.prepare.name_for_scalar_relationship` and
  315. :paramref:`.AutomapBase.prepare.name_for_collection_relationship`
  316. callable functions. It is important to note that the default relationship
  317. naming derives the name from the **the actual class name**. If you've
  318. given a particular class an explicit name by declaring it, or specified an
  319. alternate class naming scheme, that's the name from which the relationship
  320. name will be derived.
  321. 6. The classes are inspected for an existing mapped property matching these
  322. names. If one is detected on one side, but none on the other side,
  323. :class:`.AutomapBase` attempts to create a relationship on the missing side,
  324. then uses the :paramref:`_orm.relationship.back_populates`
  325. parameter in order to
  326. point the new relationship to the other side.
  327. 7. In the usual case where no relationship is on either side,
  328. :meth:`.AutomapBase.prepare` produces a :func:`_orm.relationship` on the
  329. "many-to-one" side and matches it to the other using the
  330. :paramref:`_orm.relationship.backref` parameter.
  331. 8. Production of the :func:`_orm.relationship` and optionally the
  332. :func:`.backref`
  333. is handed off to the :paramref:`.AutomapBase.prepare.generate_relationship`
  334. function, which can be supplied by the end-user in order to augment
  335. the arguments passed to :func:`_orm.relationship` or :func:`.backref` or to
  336. make use of custom implementations of these functions.
  337. Custom Relationship Arguments
  338. -----------------------------
  339. The :paramref:`.AutomapBase.prepare.generate_relationship` hook can be used
  340. to add parameters to relationships. For most cases, we can make use of the
  341. existing :func:`.automap.generate_relationship` function to return
  342. the object, after augmenting the given keyword dictionary with our own
  343. arguments.
  344. Below is an illustration of how to send
  345. :paramref:`_orm.relationship.cascade` and
  346. :paramref:`_orm.relationship.passive_deletes`
  347. options along to all one-to-many relationships::
  348. from sqlalchemy.ext.automap import generate_relationship
  349. from sqlalchemy.orm import interfaces
  350. def _gen_relationship(
  351. base, direction, return_fn, attrname, local_cls, referred_cls, **kw
  352. ):
  353. if direction is interfaces.ONETOMANY:
  354. kw["cascade"] = "all, delete-orphan"
  355. kw["passive_deletes"] = True
  356. # make use of the built-in function to actually return
  357. # the result.
  358. return generate_relationship(
  359. base, direction, return_fn, attrname, local_cls, referred_cls, **kw
  360. )
  361. from sqlalchemy.ext.automap import automap_base
  362. from sqlalchemy import create_engine
  363. # automap base
  364. Base = automap_base()
  365. engine = create_engine("sqlite:///mydatabase.db")
  366. Base.prepare(autoload_with=engine, generate_relationship=_gen_relationship)
  367. Many-to-Many relationships
  368. --------------------------
  369. :mod:`.sqlalchemy.ext.automap` will generate many-to-many relationships, e.g.
  370. those which contain a ``secondary`` argument. The process for producing these
  371. is as follows:
  372. 1. A given :class:`_schema.Table` is examined for
  373. :class:`_schema.ForeignKeyConstraint`
  374. objects, before any mapped class has been assigned to it.
  375. 2. If the table contains two and exactly two
  376. :class:`_schema.ForeignKeyConstraint`
  377. objects, and all columns within this table are members of these two
  378. :class:`_schema.ForeignKeyConstraint` objects, the table is assumed to be a
  379. "secondary" table, and will **not be mapped directly**.
  380. 3. The two (or one, for self-referential) external tables to which the
  381. :class:`_schema.Table`
  382. refers to are matched to the classes to which they will be
  383. mapped, if any.
  384. 4. If mapped classes for both sides are located, a many-to-many bi-directional
  385. :func:`_orm.relationship` / :func:`.backref`
  386. pair is created between the two
  387. classes.
  388. 5. The override logic for many-to-many works the same as that of one-to-many/
  389. many-to-one; the :func:`.generate_relationship` function is called upon
  390. to generate the structures and existing attributes will be maintained.
  391. Relationships with Inheritance
  392. ------------------------------
  393. :mod:`.sqlalchemy.ext.automap` will not generate any relationships between
  394. two classes that are in an inheritance relationship. That is, with two
  395. classes given as follows::
  396. class Employee(Base):
  397. __tablename__ = "employee"
  398. id = Column(Integer, primary_key=True)
  399. type = Column(String(50))
  400. __mapper_args__ = {
  401. "polymorphic_identity": "employee",
  402. "polymorphic_on": type,
  403. }
  404. class Engineer(Employee):
  405. __tablename__ = "engineer"
  406. id = Column(Integer, ForeignKey("employee.id"), primary_key=True)
  407. __mapper_args__ = {
  408. "polymorphic_identity": "engineer",
  409. }
  410. The foreign key from ``Engineer`` to ``Employee`` is used not for a
  411. relationship, but to establish joined inheritance between the two classes.
  412. Note that this means automap will not generate *any* relationships
  413. for foreign keys that link from a subclass to a superclass. If a mapping
  414. has actual relationships from subclass to superclass as well, those
  415. need to be explicit. Below, as we have two separate foreign keys
  416. from ``Engineer`` to ``Employee``, we need to set up both the relationship
  417. we want as well as the ``inherit_condition``, as these are not things
  418. SQLAlchemy can guess::
  419. class Employee(Base):
  420. __tablename__ = "employee"
  421. id = Column(Integer, primary_key=True)
  422. type = Column(String(50))
  423. __mapper_args__ = {
  424. "polymorphic_identity": "employee",
  425. "polymorphic_on": type,
  426. }
  427. class Engineer(Employee):
  428. __tablename__ = "engineer"
  429. id = Column(Integer, ForeignKey("employee.id"), primary_key=True)
  430. favorite_employee_id = Column(Integer, ForeignKey("employee.id"))
  431. favorite_employee = relationship(
  432. Employee, foreign_keys=favorite_employee_id
  433. )
  434. __mapper_args__ = {
  435. "polymorphic_identity": "engineer",
  436. "inherit_condition": id == Employee.id,
  437. }
  438. Handling Simple Naming Conflicts
  439. --------------------------------
  440. In the case of naming conflicts during mapping, override any of
  441. :func:`.classname_for_table`, :func:`.name_for_scalar_relationship`,
  442. and :func:`.name_for_collection_relationship` as needed. For example, if
  443. automap is attempting to name a many-to-one relationship the same as an
  444. existing column, an alternate convention can be conditionally selected. Given
  445. a schema:
  446. .. sourcecode:: sql
  447. CREATE TABLE table_a (
  448. id INTEGER PRIMARY KEY
  449. );
  450. CREATE TABLE table_b (
  451. id INTEGER PRIMARY KEY,
  452. table_a INTEGER,
  453. FOREIGN KEY(table_a) REFERENCES table_a(id)
  454. );
  455. The above schema will first automap the ``table_a`` table as a class named
  456. ``table_a``; it will then automap a relationship onto the class for ``table_b``
  457. with the same name as this related class, e.g. ``table_a``. This
  458. relationship name conflicts with the mapping column ``table_b.table_a``,
  459. and will emit an error on mapping.
  460. We can resolve this conflict by using an underscore as follows::
  461. def name_for_scalar_relationship(
  462. base, local_cls, referred_cls, constraint
  463. ):
  464. name = referred_cls.__name__.lower()
  465. local_table = local_cls.__table__
  466. if name in local_table.columns:
  467. newname = name + "_"
  468. warnings.warn(
  469. "Already detected name %s present. using %s" % (name, newname)
  470. )
  471. return newname
  472. return name
  473. Base.prepare(
  474. autoload_with=engine,
  475. name_for_scalar_relationship=name_for_scalar_relationship,
  476. )
  477. Alternatively, we can change the name on the column side. The columns
  478. that are mapped can be modified using the technique described at
  479. :ref:`mapper_column_distinct_names`, by assigning the column explicitly
  480. to a new name::
  481. Base = automap_base()
  482. class TableB(Base):
  483. __tablename__ = "table_b"
  484. _table_a = Column("table_a", ForeignKey("table_a.id"))
  485. Base.prepare(autoload_with=engine)
  486. Using Automap with Explicit Declarations
  487. ========================================
  488. As noted previously, automap has no dependency on reflection, and can make
  489. use of any collection of :class:`_schema.Table` objects within a
  490. :class:`_schema.MetaData`
  491. collection. From this, it follows that automap can also be used
  492. generate missing relationships given an otherwise complete model that fully
  493. defines table metadata::
  494. from sqlalchemy.ext.automap import automap_base
  495. from sqlalchemy import Column, Integer, String, ForeignKey
  496. Base = automap_base()
  497. class User(Base):
  498. __tablename__ = "user"
  499. id = Column(Integer, primary_key=True)
  500. name = Column(String)
  501. class Address(Base):
  502. __tablename__ = "address"
  503. id = Column(Integer, primary_key=True)
  504. email = Column(String)
  505. user_id = Column(ForeignKey("user.id"))
  506. # produce relationships
  507. Base.prepare()
  508. # mapping is complete, with "address_collection" and
  509. # "user" relationships
  510. a1 = Address(email="u1")
  511. a2 = Address(email="u2")
  512. u1 = User(address_collection=[a1, a2])
  513. assert a1.user is u1
  514. Above, given mostly complete ``User`` and ``Address`` mappings, the
  515. :class:`_schema.ForeignKey` which we defined on ``Address.user_id`` allowed a
  516. bidirectional relationship pair ``Address.user`` and
  517. ``User.address_collection`` to be generated on the mapped classes.
  518. Note that when subclassing :class:`.AutomapBase`,
  519. the :meth:`.AutomapBase.prepare` method is required; if not called, the classes
  520. we've declared are in an un-mapped state.
  521. .. _automap_intercepting_columns:
  522. Intercepting Column Definitions
  523. ===============================
  524. The :class:`_schema.MetaData` and :class:`_schema.Table` objects support an
  525. event hook :meth:`_events.DDLEvents.column_reflect` that may be used to intercept
  526. the information reflected about a database column before the :class:`_schema.Column`
  527. object is constructed. For example if we wanted to map columns using a
  528. naming convention such as ``"attr_<columnname>"``, the event could
  529. be applied as::
  530. @event.listens_for(Base.metadata, "column_reflect")
  531. def column_reflect(inspector, table, column_info):
  532. # set column.key = "attr_<lower_case_name>"
  533. column_info["key"] = "attr_%s" % column_info["name"].lower()
  534. # run reflection
  535. Base.prepare(autoload_with=engine)
  536. .. versionadded:: 1.4.0b2 the :meth:`_events.DDLEvents.column_reflect` event
  537. may be applied to a :class:`_schema.MetaData` object.
  538. .. seealso::
  539. :meth:`_events.DDLEvents.column_reflect`
  540. :ref:`mapper_automated_reflection_schemes` - in the ORM mapping documentation
  541. """ # noqa
  542. from __future__ import annotations
  543. import dataclasses
  544. from typing import Any
  545. from typing import Callable
  546. from typing import cast
  547. from typing import ClassVar
  548. from typing import Dict
  549. from typing import List
  550. from typing import NoReturn
  551. from typing import Optional
  552. from typing import overload
  553. from typing import Set
  554. from typing import Tuple
  555. from typing import Type
  556. from typing import TYPE_CHECKING
  557. from typing import TypeVar
  558. from typing import Union
  559. from .. import util
  560. from ..orm import backref
  561. from ..orm import declarative_base as _declarative_base
  562. from ..orm import exc as orm_exc
  563. from ..orm import interfaces
  564. from ..orm import relationship
  565. from ..orm.decl_base import _DeferredMapperConfig
  566. from ..orm.mapper import _CONFIGURE_MUTEX
  567. from ..schema import ForeignKeyConstraint
  568. from ..sql import and_
  569. from ..util import Properties
  570. from ..util.typing import Protocol
  571. if TYPE_CHECKING:
  572. from ..engine.base import Engine
  573. from ..orm.base import RelationshipDirection
  574. from ..orm.relationships import ORMBackrefArgument
  575. from ..orm.relationships import Relationship
  576. from ..sql.schema import Column
  577. from ..sql.schema import MetaData
  578. from ..sql.schema import Table
  579. from ..util import immutabledict
  580. _KT = TypeVar("_KT", bound=Any)
  581. _VT = TypeVar("_VT", bound=Any)
  582. class PythonNameForTableType(Protocol):
  583. def __call__(
  584. self, base: Type[Any], tablename: str, table: Table
  585. ) -> str: ...
  586. def classname_for_table(
  587. base: Type[Any],
  588. tablename: str,
  589. table: Table,
  590. ) -> str:
  591. """Return the class name that should be used, given the name
  592. of a table.
  593. The default implementation is::
  594. return str(tablename)
  595. Alternate implementations can be specified using the
  596. :paramref:`.AutomapBase.prepare.classname_for_table`
  597. parameter.
  598. :param base: the :class:`.AutomapBase` class doing the prepare.
  599. :param tablename: string name of the :class:`_schema.Table`.
  600. :param table: the :class:`_schema.Table` object itself.
  601. :return: a string class name.
  602. .. note::
  603. In Python 2, the string used for the class name **must** be a
  604. non-Unicode object, e.g. a ``str()`` object. The ``.name`` attribute
  605. of :class:`_schema.Table` is typically a Python unicode subclass,
  606. so the
  607. ``str()`` function should be applied to this name, after accounting for
  608. any non-ASCII characters.
  609. """
  610. return str(tablename)
  611. class NameForScalarRelationshipType(Protocol):
  612. def __call__(
  613. self,
  614. base: Type[Any],
  615. local_cls: Type[Any],
  616. referred_cls: Type[Any],
  617. constraint: ForeignKeyConstraint,
  618. ) -> str: ...
  619. def name_for_scalar_relationship(
  620. base: Type[Any],
  621. local_cls: Type[Any],
  622. referred_cls: Type[Any],
  623. constraint: ForeignKeyConstraint,
  624. ) -> str:
  625. """Return the attribute name that should be used to refer from one
  626. class to another, for a scalar object reference.
  627. The default implementation is::
  628. return referred_cls.__name__.lower()
  629. Alternate implementations can be specified using the
  630. :paramref:`.AutomapBase.prepare.name_for_scalar_relationship`
  631. parameter.
  632. :param base: the :class:`.AutomapBase` class doing the prepare.
  633. :param local_cls: the class to be mapped on the local side.
  634. :param referred_cls: the class to be mapped on the referring side.
  635. :param constraint: the :class:`_schema.ForeignKeyConstraint` that is being
  636. inspected to produce this relationship.
  637. """
  638. return referred_cls.__name__.lower()
  639. class NameForCollectionRelationshipType(Protocol):
  640. def __call__(
  641. self,
  642. base: Type[Any],
  643. local_cls: Type[Any],
  644. referred_cls: Type[Any],
  645. constraint: ForeignKeyConstraint,
  646. ) -> str: ...
  647. def name_for_collection_relationship(
  648. base: Type[Any],
  649. local_cls: Type[Any],
  650. referred_cls: Type[Any],
  651. constraint: ForeignKeyConstraint,
  652. ) -> str:
  653. """Return the attribute name that should be used to refer from one
  654. class to another, for a collection reference.
  655. The default implementation is::
  656. return referred_cls.__name__.lower() + "_collection"
  657. Alternate implementations
  658. can be specified using the
  659. :paramref:`.AutomapBase.prepare.name_for_collection_relationship`
  660. parameter.
  661. :param base: the :class:`.AutomapBase` class doing the prepare.
  662. :param local_cls: the class to be mapped on the local side.
  663. :param referred_cls: the class to be mapped on the referring side.
  664. :param constraint: the :class:`_schema.ForeignKeyConstraint` that is being
  665. inspected to produce this relationship.
  666. """
  667. return referred_cls.__name__.lower() + "_collection"
  668. class GenerateRelationshipType(Protocol):
  669. @overload
  670. def __call__(
  671. self,
  672. base: Type[Any],
  673. direction: RelationshipDirection,
  674. return_fn: Callable[..., Relationship[Any]],
  675. attrname: str,
  676. local_cls: Type[Any],
  677. referred_cls: Type[Any],
  678. **kw: Any,
  679. ) -> Relationship[Any]: ...
  680. @overload
  681. def __call__(
  682. self,
  683. base: Type[Any],
  684. direction: RelationshipDirection,
  685. return_fn: Callable[..., ORMBackrefArgument],
  686. attrname: str,
  687. local_cls: Type[Any],
  688. referred_cls: Type[Any],
  689. **kw: Any,
  690. ) -> ORMBackrefArgument: ...
  691. def __call__(
  692. self,
  693. base: Type[Any],
  694. direction: RelationshipDirection,
  695. return_fn: Union[
  696. Callable[..., Relationship[Any]], Callable[..., ORMBackrefArgument]
  697. ],
  698. attrname: str,
  699. local_cls: Type[Any],
  700. referred_cls: Type[Any],
  701. **kw: Any,
  702. ) -> Union[ORMBackrefArgument, Relationship[Any]]: ...
  703. @overload
  704. def generate_relationship(
  705. base: Type[Any],
  706. direction: RelationshipDirection,
  707. return_fn: Callable[..., Relationship[Any]],
  708. attrname: str,
  709. local_cls: Type[Any],
  710. referred_cls: Type[Any],
  711. **kw: Any,
  712. ) -> Relationship[Any]: ...
  713. @overload
  714. def generate_relationship(
  715. base: Type[Any],
  716. direction: RelationshipDirection,
  717. return_fn: Callable[..., ORMBackrefArgument],
  718. attrname: str,
  719. local_cls: Type[Any],
  720. referred_cls: Type[Any],
  721. **kw: Any,
  722. ) -> ORMBackrefArgument: ...
  723. def generate_relationship(
  724. base: Type[Any],
  725. direction: RelationshipDirection,
  726. return_fn: Union[
  727. Callable[..., Relationship[Any]], Callable[..., ORMBackrefArgument]
  728. ],
  729. attrname: str,
  730. local_cls: Type[Any],
  731. referred_cls: Type[Any],
  732. **kw: Any,
  733. ) -> Union[Relationship[Any], ORMBackrefArgument]:
  734. r"""Generate a :func:`_orm.relationship` or :func:`.backref`
  735. on behalf of two
  736. mapped classes.
  737. An alternate implementation of this function can be specified using the
  738. :paramref:`.AutomapBase.prepare.generate_relationship` parameter.
  739. The default implementation of this function is as follows::
  740. if return_fn is backref:
  741. return return_fn(attrname, **kw)
  742. elif return_fn is relationship:
  743. return return_fn(referred_cls, **kw)
  744. else:
  745. raise TypeError("Unknown relationship function: %s" % return_fn)
  746. :param base: the :class:`.AutomapBase` class doing the prepare.
  747. :param direction: indicate the "direction" of the relationship; this will
  748. be one of :data:`.ONETOMANY`, :data:`.MANYTOONE`, :data:`.MANYTOMANY`.
  749. :param return_fn: the function that is used by default to create the
  750. relationship. This will be either :func:`_orm.relationship` or
  751. :func:`.backref`. The :func:`.backref` function's result will be used to
  752. produce a new :func:`_orm.relationship` in a second step,
  753. so it is critical
  754. that user-defined implementations correctly differentiate between the two
  755. functions, if a custom relationship function is being used.
  756. :param attrname: the attribute name to which this relationship is being
  757. assigned. If the value of :paramref:`.generate_relationship.return_fn` is
  758. the :func:`.backref` function, then this name is the name that is being
  759. assigned to the backref.
  760. :param local_cls: the "local" class to which this relationship or backref
  761. will be locally present.
  762. :param referred_cls: the "referred" class to which the relationship or
  763. backref refers to.
  764. :param \**kw: all additional keyword arguments are passed along to the
  765. function.
  766. :return: a :func:`_orm.relationship` or :func:`.backref` construct,
  767. as dictated
  768. by the :paramref:`.generate_relationship.return_fn` parameter.
  769. """
  770. if return_fn is backref:
  771. return return_fn(attrname, **kw)
  772. elif return_fn is relationship:
  773. return return_fn(referred_cls, **kw)
  774. else:
  775. raise TypeError("Unknown relationship function: %s" % return_fn)
  776. ByModuleProperties = Properties[Union["ByModuleProperties", Type[Any]]]
  777. class AutomapBase:
  778. """Base class for an "automap" schema.
  779. The :class:`.AutomapBase` class can be compared to the "declarative base"
  780. class that is produced by the :func:`.declarative.declarative_base`
  781. function. In practice, the :class:`.AutomapBase` class is always used
  782. as a mixin along with an actual declarative base.
  783. A new subclassable :class:`.AutomapBase` is typically instantiated
  784. using the :func:`.automap_base` function.
  785. .. seealso::
  786. :ref:`automap_toplevel`
  787. """
  788. __abstract__ = True
  789. classes: ClassVar[Properties[Type[Any]]]
  790. """An instance of :class:`.util.Properties` containing classes.
  791. This object behaves much like the ``.c`` collection on a table. Classes
  792. are present under the name they were given, e.g.::
  793. Base = automap_base()
  794. Base.prepare(autoload_with=some_engine)
  795. User, Address = Base.classes.User, Base.classes.Address
  796. For class names that overlap with a method name of
  797. :class:`.util.Properties`, such as ``items()``, the getitem form
  798. is also supported::
  799. Item = Base.classes["items"]
  800. """
  801. by_module: ClassVar[ByModuleProperties]
  802. """An instance of :class:`.util.Properties` containing a hierarchal
  803. structure of dot-separated module names linked to classes.
  804. This collection is an alternative to the :attr:`.AutomapBase.classes`
  805. collection that is useful when making use of the
  806. :paramref:`.AutomapBase.prepare.modulename_for_table` parameter, which will
  807. apply distinct ``__module__`` attributes to generated classes.
  808. The default ``__module__`` an automap-generated class is
  809. ``sqlalchemy.ext.automap``; to access this namespace using
  810. :attr:`.AutomapBase.by_module` looks like::
  811. User = Base.by_module.sqlalchemy.ext.automap.User
  812. If a class had a ``__module__`` of ``mymodule.account``, accessing
  813. this namespace looks like::
  814. MyClass = Base.by_module.mymodule.account.MyClass
  815. .. versionadded:: 2.0
  816. .. seealso::
  817. :ref:`automap_by_module`
  818. """
  819. metadata: ClassVar[MetaData]
  820. """Refers to the :class:`_schema.MetaData` collection that will be used
  821. for new :class:`_schema.Table` objects.
  822. .. seealso::
  823. :ref:`orm_declarative_metadata`
  824. """
  825. _sa_automapbase_bookkeeping: ClassVar[_Bookkeeping]
  826. @classmethod
  827. @util.deprecated_params(
  828. engine=(
  829. "2.0",
  830. "The :paramref:`_automap.AutomapBase.prepare.engine` parameter "
  831. "is deprecated and will be removed in a future release. "
  832. "Please use the "
  833. ":paramref:`_automap.AutomapBase.prepare.autoload_with` "
  834. "parameter.",
  835. ),
  836. reflect=(
  837. "2.0",
  838. "The :paramref:`_automap.AutomapBase.prepare.reflect` "
  839. "parameter is deprecated and will be removed in a future "
  840. "release. Reflection is enabled when "
  841. ":paramref:`_automap.AutomapBase.prepare.autoload_with` "
  842. "is passed.",
  843. ),
  844. )
  845. def prepare(
  846. cls: Type[AutomapBase],
  847. autoload_with: Optional[Engine] = None,
  848. engine: Optional[Any] = None,
  849. reflect: bool = False,
  850. schema: Optional[str] = None,
  851. classname_for_table: Optional[PythonNameForTableType] = None,
  852. modulename_for_table: Optional[PythonNameForTableType] = None,
  853. collection_class: Optional[Any] = None,
  854. name_for_scalar_relationship: Optional[
  855. NameForScalarRelationshipType
  856. ] = None,
  857. name_for_collection_relationship: Optional[
  858. NameForCollectionRelationshipType
  859. ] = None,
  860. generate_relationship: Optional[GenerateRelationshipType] = None,
  861. reflection_options: Union[
  862. Dict[_KT, _VT], immutabledict[_KT, _VT]
  863. ] = util.EMPTY_DICT,
  864. ) -> None:
  865. """Extract mapped classes and relationships from the
  866. :class:`_schema.MetaData` and perform mappings.
  867. For full documentation and examples see
  868. :ref:`automap_basic_use`.
  869. :param autoload_with: an :class:`_engine.Engine` or
  870. :class:`_engine.Connection` with which
  871. to perform schema reflection; when specified, the
  872. :meth:`_schema.MetaData.reflect` method will be invoked within
  873. the scope of this method.
  874. :param engine: legacy; use :paramref:`.AutomapBase.autoload_with`.
  875. Used to indicate the :class:`_engine.Engine` or
  876. :class:`_engine.Connection` with which to reflect tables with,
  877. if :paramref:`.AutomapBase.reflect` is True.
  878. :param reflect: legacy; use :paramref:`.AutomapBase.autoload_with`.
  879. Indicates that :meth:`_schema.MetaData.reflect` should be invoked.
  880. :param classname_for_table: callable function which will be used to
  881. produce new class names, given a table name. Defaults to
  882. :func:`.classname_for_table`.
  883. :param modulename_for_table: callable function which will be used to
  884. produce the effective ``__module__`` for an internally generated
  885. class, to allow for multiple classes of the same name in a single
  886. automap base which would be in different "modules".
  887. Defaults to ``None``, which will indicate that ``__module__`` will not
  888. be set explicitly; the Python runtime will use the value
  889. ``sqlalchemy.ext.automap`` for these classes.
  890. When assigning ``__module__`` to generated classes, they can be
  891. accessed based on dot-separated module names using the
  892. :attr:`.AutomapBase.by_module` collection. Classes that have
  893. an explicit ``__module_`` assigned using this hook do **not** get
  894. placed into the :attr:`.AutomapBase.classes` collection, only
  895. into :attr:`.AutomapBase.by_module`.
  896. .. versionadded:: 2.0
  897. .. seealso::
  898. :ref:`automap_by_module`
  899. :param name_for_scalar_relationship: callable function which will be
  900. used to produce relationship names for scalar relationships. Defaults
  901. to :func:`.name_for_scalar_relationship`.
  902. :param name_for_collection_relationship: callable function which will
  903. be used to produce relationship names for collection-oriented
  904. relationships. Defaults to :func:`.name_for_collection_relationship`.
  905. :param generate_relationship: callable function which will be used to
  906. actually generate :func:`_orm.relationship` and :func:`.backref`
  907. constructs. Defaults to :func:`.generate_relationship`.
  908. :param collection_class: the Python collection class that will be used
  909. when a new :func:`_orm.relationship`
  910. object is created that represents a
  911. collection. Defaults to ``list``.
  912. :param schema: Schema name to reflect when reflecting tables using
  913. the :paramref:`.AutomapBase.prepare.autoload_with` parameter. The name
  914. is passed to the :paramref:`_schema.MetaData.reflect.schema` parameter
  915. of :meth:`_schema.MetaData.reflect`. When omitted, the default schema
  916. in use by the database connection is used.
  917. .. note:: The :paramref:`.AutomapBase.prepare.schema`
  918. parameter supports reflection of a single schema at a time.
  919. In order to include tables from many schemas, use
  920. multiple calls to :meth:`.AutomapBase.prepare`.
  921. For an overview of multiple-schema automap including the use
  922. of additional naming conventions to resolve table name
  923. conflicts, see the section :ref:`automap_by_module`.
  924. .. versionadded:: 2.0 :meth:`.AutomapBase.prepare` supports being
  925. directly invoked any number of times, keeping track of tables
  926. that have already been processed to avoid processing them
  927. a second time.
  928. :param reflection_options: When present, this dictionary of options
  929. will be passed to :meth:`_schema.MetaData.reflect`
  930. to supply general reflection-specific options like ``only`` and/or
  931. dialect-specific options like ``oracle_resolve_synonyms``.
  932. .. versionadded:: 1.4
  933. """
  934. for mr in cls.__mro__:
  935. if "_sa_automapbase_bookkeeping" in mr.__dict__:
  936. automap_base = cast("Type[AutomapBase]", mr)
  937. break
  938. else:
  939. assert False, "Can't locate automap base in class hierarchy"
  940. glbls = globals()
  941. if classname_for_table is None:
  942. classname_for_table = glbls["classname_for_table"]
  943. if name_for_scalar_relationship is None:
  944. name_for_scalar_relationship = glbls[
  945. "name_for_scalar_relationship"
  946. ]
  947. if name_for_collection_relationship is None:
  948. name_for_collection_relationship = glbls[
  949. "name_for_collection_relationship"
  950. ]
  951. if generate_relationship is None:
  952. generate_relationship = glbls["generate_relationship"]
  953. if collection_class is None:
  954. collection_class = list
  955. if autoload_with:
  956. reflect = True
  957. if engine:
  958. autoload_with = engine
  959. if reflect:
  960. assert autoload_with
  961. opts = dict(
  962. schema=schema,
  963. extend_existing=True,
  964. autoload_replace=False,
  965. )
  966. if reflection_options:
  967. opts.update(reflection_options)
  968. cls.metadata.reflect(autoload_with, **opts) # type: ignore[arg-type] # noqa: E501
  969. with _CONFIGURE_MUTEX:
  970. table_to_map_config: Union[
  971. Dict[Optional[Table], _DeferredMapperConfig],
  972. Dict[Table, _DeferredMapperConfig],
  973. ] = {
  974. cast("Table", m.local_table): m
  975. for m in _DeferredMapperConfig.classes_for_base(
  976. cls, sort=False
  977. )
  978. }
  979. many_to_many: List[
  980. Tuple[Table, Table, List[ForeignKeyConstraint], Table]
  981. ]
  982. many_to_many = []
  983. bookkeeping = automap_base._sa_automapbase_bookkeeping
  984. metadata_tables = cls.metadata.tables
  985. for table_key in set(metadata_tables).difference(
  986. bookkeeping.table_keys
  987. ):
  988. table = metadata_tables[table_key]
  989. bookkeeping.table_keys.add(table_key)
  990. lcl_m2m, rem_m2m, m2m_const = _is_many_to_many(cls, table)
  991. if lcl_m2m is not None:
  992. assert rem_m2m is not None
  993. assert m2m_const is not None
  994. many_to_many.append((lcl_m2m, rem_m2m, m2m_const, table))
  995. elif not table.primary_key:
  996. continue
  997. elif table not in table_to_map_config:
  998. clsdict: Dict[str, Any] = {"__table__": table}
  999. if modulename_for_table is not None:
  1000. new_module = modulename_for_table(
  1001. cls, table.name, table
  1002. )
  1003. if new_module is not None:
  1004. clsdict["__module__"] = new_module
  1005. else:
  1006. new_module = None
  1007. newname = classname_for_table(cls, table.name, table)
  1008. if new_module is None and newname in cls.classes:
  1009. util.warn(
  1010. "Ignoring duplicate class name "
  1011. f"'{newname}' "
  1012. "received in automap base for table "
  1013. f"{table.key} without "
  1014. "``__module__`` being set; consider using the "
  1015. "``modulename_for_table`` hook"
  1016. )
  1017. continue
  1018. mapped_cls = type(
  1019. newname,
  1020. (automap_base,),
  1021. clsdict,
  1022. )
  1023. map_config = _DeferredMapperConfig.config_for_cls(
  1024. mapped_cls
  1025. )
  1026. assert map_config.cls.__name__ == newname
  1027. if new_module is None:
  1028. cls.classes[newname] = mapped_cls
  1029. by_module_properties: ByModuleProperties = cls.by_module
  1030. for token in map_config.cls.__module__.split("."):
  1031. if token not in by_module_properties:
  1032. by_module_properties[token] = util.Properties({})
  1033. props = by_module_properties[token]
  1034. # we can assert this because the clsregistry
  1035. # module would have raised if there was a mismatch
  1036. # between modules/classes already.
  1037. # see test_cls_schema_name_conflict
  1038. assert isinstance(props, Properties)
  1039. by_module_properties = props
  1040. by_module_properties[map_config.cls.__name__] = mapped_cls
  1041. table_to_map_config[table] = map_config
  1042. for map_config in table_to_map_config.values():
  1043. _relationships_for_fks(
  1044. automap_base,
  1045. map_config,
  1046. table_to_map_config,
  1047. collection_class,
  1048. name_for_scalar_relationship,
  1049. name_for_collection_relationship,
  1050. generate_relationship,
  1051. )
  1052. for lcl_m2m, rem_m2m, m2m_const, table in many_to_many:
  1053. _m2m_relationship(
  1054. automap_base,
  1055. lcl_m2m,
  1056. rem_m2m,
  1057. m2m_const,
  1058. table,
  1059. table_to_map_config,
  1060. collection_class,
  1061. name_for_scalar_relationship,
  1062. name_for_collection_relationship,
  1063. generate_relationship,
  1064. )
  1065. for map_config in _DeferredMapperConfig.classes_for_base(
  1066. automap_base
  1067. ):
  1068. map_config.map()
  1069. _sa_decl_prepare = True
  1070. """Indicate that the mapping of classes should be deferred.
  1071. The presence of this attribute name indicates to declarative
  1072. that the call to mapper() should not occur immediately; instead,
  1073. information about the table and attributes to be mapped are gathered
  1074. into an internal structure called _DeferredMapperConfig. These
  1075. objects can be collected later using classes_for_base(), additional
  1076. mapping decisions can be made, and then the map() method will actually
  1077. apply the mapping.
  1078. The only real reason this deferral of the whole
  1079. thing is needed is to support primary key columns that aren't reflected
  1080. yet when the class is declared; everything else can theoretically be
  1081. added to the mapper later. However, the _DeferredMapperConfig is a
  1082. nice interface in any case which exists at that not usually exposed point
  1083. at which declarative has the class and the Table but hasn't called
  1084. mapper() yet.
  1085. """
  1086. @classmethod
  1087. def _sa_raise_deferred_config(cls) -> NoReturn:
  1088. raise orm_exc.UnmappedClassError(
  1089. cls,
  1090. msg="Class %s is a subclass of AutomapBase. "
  1091. "Mappings are not produced until the .prepare() "
  1092. "method is called on the class hierarchy."
  1093. % orm_exc._safe_cls_name(cls),
  1094. )
  1095. @dataclasses.dataclass
  1096. class _Bookkeeping:
  1097. __slots__ = ("table_keys",)
  1098. table_keys: Set[str]
  1099. def automap_base(
  1100. declarative_base: Optional[Type[Any]] = None, **kw: Any
  1101. ) -> Any:
  1102. r"""Produce a declarative automap base.
  1103. This function produces a new base class that is a product of the
  1104. :class:`.AutomapBase` class as well a declarative base produced by
  1105. :func:`.declarative.declarative_base`.
  1106. All parameters other than ``declarative_base`` are keyword arguments
  1107. that are passed directly to the :func:`.declarative.declarative_base`
  1108. function.
  1109. :param declarative_base: an existing class produced by
  1110. :func:`.declarative.declarative_base`. When this is passed, the function
  1111. no longer invokes :func:`.declarative.declarative_base` itself, and all
  1112. other keyword arguments are ignored.
  1113. :param \**kw: keyword arguments are passed along to
  1114. :func:`.declarative.declarative_base`.
  1115. """
  1116. if declarative_base is None:
  1117. Base = _declarative_base(**kw)
  1118. else:
  1119. Base = declarative_base
  1120. return type(
  1121. Base.__name__,
  1122. (AutomapBase, Base),
  1123. {
  1124. "__abstract__": True,
  1125. "classes": util.Properties({}),
  1126. "by_module": util.Properties({}),
  1127. "_sa_automapbase_bookkeeping": _Bookkeeping(set()),
  1128. },
  1129. )
  1130. def _is_many_to_many(
  1131. automap_base: Type[Any], table: Table
  1132. ) -> Tuple[
  1133. Optional[Table], Optional[Table], Optional[list[ForeignKeyConstraint]]
  1134. ]:
  1135. fk_constraints = [
  1136. const
  1137. for const in table.constraints
  1138. if isinstance(const, ForeignKeyConstraint)
  1139. ]
  1140. if len(fk_constraints) != 2:
  1141. return None, None, None
  1142. cols: List[Column[Any]] = sum(
  1143. [
  1144. [fk.parent for fk in fk_constraint.elements]
  1145. for fk_constraint in fk_constraints
  1146. ],
  1147. [],
  1148. )
  1149. if set(cols) != set(table.c):
  1150. return None, None, None
  1151. return (
  1152. fk_constraints[0].elements[0].column.table,
  1153. fk_constraints[1].elements[0].column.table,
  1154. fk_constraints,
  1155. )
  1156. def _relationships_for_fks(
  1157. automap_base: Type[Any],
  1158. map_config: _DeferredMapperConfig,
  1159. table_to_map_config: Union[
  1160. Dict[Optional[Table], _DeferredMapperConfig],
  1161. Dict[Table, _DeferredMapperConfig],
  1162. ],
  1163. collection_class: type,
  1164. name_for_scalar_relationship: NameForScalarRelationshipType,
  1165. name_for_collection_relationship: NameForCollectionRelationshipType,
  1166. generate_relationship: GenerateRelationshipType,
  1167. ) -> None:
  1168. local_table = cast("Optional[Table]", map_config.local_table)
  1169. local_cls = cast(
  1170. "Optional[Type[Any]]", map_config.cls
  1171. ) # derived from a weakref, may be None
  1172. if local_table is None or local_cls is None:
  1173. return
  1174. for constraint in local_table.constraints:
  1175. if isinstance(constraint, ForeignKeyConstraint):
  1176. fks = constraint.elements
  1177. referred_table = fks[0].column.table
  1178. referred_cfg = table_to_map_config.get(referred_table, None)
  1179. if referred_cfg is None:
  1180. continue
  1181. referred_cls = referred_cfg.cls
  1182. if local_cls is not referred_cls and issubclass(
  1183. local_cls, referred_cls
  1184. ):
  1185. continue
  1186. relationship_name = name_for_scalar_relationship(
  1187. automap_base, local_cls, referred_cls, constraint
  1188. )
  1189. backref_name = name_for_collection_relationship(
  1190. automap_base, referred_cls, local_cls, constraint
  1191. )
  1192. o2m_kws: Dict[str, Union[str, bool]] = {}
  1193. nullable = False not in {fk.parent.nullable for fk in fks}
  1194. if not nullable:
  1195. o2m_kws["cascade"] = "all, delete-orphan"
  1196. if (
  1197. constraint.ondelete
  1198. and constraint.ondelete.lower() == "cascade"
  1199. ):
  1200. o2m_kws["passive_deletes"] = True
  1201. else:
  1202. if (
  1203. constraint.ondelete
  1204. and constraint.ondelete.lower() == "set null"
  1205. ):
  1206. o2m_kws["passive_deletes"] = True
  1207. create_backref = backref_name not in referred_cfg.properties
  1208. if relationship_name not in map_config.properties:
  1209. if create_backref:
  1210. backref_obj = generate_relationship(
  1211. automap_base,
  1212. interfaces.ONETOMANY,
  1213. backref,
  1214. backref_name,
  1215. referred_cls,
  1216. local_cls,
  1217. collection_class=collection_class,
  1218. **o2m_kws,
  1219. )
  1220. else:
  1221. backref_obj = None
  1222. rel = generate_relationship(
  1223. automap_base,
  1224. interfaces.MANYTOONE,
  1225. relationship,
  1226. relationship_name,
  1227. local_cls,
  1228. referred_cls,
  1229. foreign_keys=[fk.parent for fk in constraint.elements],
  1230. backref=backref_obj,
  1231. remote_side=[fk.column for fk in constraint.elements],
  1232. )
  1233. if rel is not None:
  1234. map_config.properties[relationship_name] = rel
  1235. if not create_backref:
  1236. referred_cfg.properties[
  1237. backref_name
  1238. ].back_populates = relationship_name # type: ignore[union-attr] # noqa: E501
  1239. elif create_backref:
  1240. rel = generate_relationship(
  1241. automap_base,
  1242. interfaces.ONETOMANY,
  1243. relationship,
  1244. backref_name,
  1245. referred_cls,
  1246. local_cls,
  1247. foreign_keys=[fk.parent for fk in constraint.elements],
  1248. back_populates=relationship_name,
  1249. collection_class=collection_class,
  1250. **o2m_kws,
  1251. )
  1252. if rel is not None:
  1253. referred_cfg.properties[backref_name] = rel
  1254. map_config.properties[
  1255. relationship_name
  1256. ].back_populates = backref_name # type: ignore[union-attr]
  1257. def _m2m_relationship(
  1258. automap_base: Type[Any],
  1259. lcl_m2m: Table,
  1260. rem_m2m: Table,
  1261. m2m_const: List[ForeignKeyConstraint],
  1262. table: Table,
  1263. table_to_map_config: Union[
  1264. Dict[Optional[Table], _DeferredMapperConfig],
  1265. Dict[Table, _DeferredMapperConfig],
  1266. ],
  1267. collection_class: type,
  1268. name_for_scalar_relationship: NameForCollectionRelationshipType,
  1269. name_for_collection_relationship: NameForCollectionRelationshipType,
  1270. generate_relationship: GenerateRelationshipType,
  1271. ) -> None:
  1272. map_config = table_to_map_config.get(lcl_m2m, None)
  1273. referred_cfg = table_to_map_config.get(rem_m2m, None)
  1274. if map_config is None or referred_cfg is None:
  1275. return
  1276. local_cls = map_config.cls
  1277. referred_cls = referred_cfg.cls
  1278. relationship_name = name_for_collection_relationship(
  1279. automap_base, local_cls, referred_cls, m2m_const[0]
  1280. )
  1281. backref_name = name_for_collection_relationship(
  1282. automap_base, referred_cls, local_cls, m2m_const[1]
  1283. )
  1284. create_backref = backref_name not in referred_cfg.properties
  1285. if table in table_to_map_config:
  1286. overlaps = "__*"
  1287. else:
  1288. overlaps = None
  1289. if relationship_name not in map_config.properties:
  1290. if create_backref:
  1291. backref_obj = generate_relationship(
  1292. automap_base,
  1293. interfaces.MANYTOMANY,
  1294. backref,
  1295. backref_name,
  1296. referred_cls,
  1297. local_cls,
  1298. collection_class=collection_class,
  1299. overlaps=overlaps,
  1300. )
  1301. else:
  1302. backref_obj = None
  1303. rel = generate_relationship(
  1304. automap_base,
  1305. interfaces.MANYTOMANY,
  1306. relationship,
  1307. relationship_name,
  1308. local_cls,
  1309. referred_cls,
  1310. overlaps=overlaps,
  1311. secondary=table,
  1312. primaryjoin=and_(
  1313. fk.column == fk.parent for fk in m2m_const[0].elements
  1314. ), # type: ignore [arg-type]
  1315. secondaryjoin=and_(
  1316. fk.column == fk.parent for fk in m2m_const[1].elements
  1317. ), # type: ignore [arg-type]
  1318. backref=backref_obj,
  1319. collection_class=collection_class,
  1320. )
  1321. if rel is not None:
  1322. map_config.properties[relationship_name] = rel
  1323. if not create_backref:
  1324. referred_cfg.properties[
  1325. backref_name
  1326. ].back_populates = relationship_name # type: ignore[union-attr] # noqa: E501
  1327. elif create_backref:
  1328. rel = generate_relationship(
  1329. automap_base,
  1330. interfaces.MANYTOMANY,
  1331. relationship,
  1332. backref_name,
  1333. referred_cls,
  1334. local_cls,
  1335. overlaps=overlaps,
  1336. secondary=table,
  1337. primaryjoin=and_(
  1338. fk.column == fk.parent for fk in m2m_const[1].elements
  1339. ), # type: ignore [arg-type]
  1340. secondaryjoin=and_(
  1341. fk.column == fk.parent for fk in m2m_const[0].elements
  1342. ), # type: ignore [arg-type]
  1343. back_populates=relationship_name,
  1344. collection_class=collection_class,
  1345. )
  1346. if rel is not None:
  1347. referred_cfg.properties[backref_name] = rel
  1348. map_config.properties[
  1349. relationship_name
  1350. ].back_populates = backref_name # type: ignore[union-attr]