automap.py 60 KB

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