Team Ai
Datasetpublic

codekingpro/portable-devtools

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

Showing the first 1,200 of 1702 lines. Download the file for the rest.

codekingpro/portable-devtools · Team Ai