You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					1225 lines
				
				44 KiB
			
		
		
			
		
	
	
					1225 lines
				
				44 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								# ext/automap.py
							 | 
						||
| 
								 | 
							
								# Copyright (C) 2005-2022 the SQLAlchemy authors and contributors
							 | 
						||
| 
								 | 
							
								# <see AUTHORS file>
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# This module is part of SQLAlchemy and is released under
							 | 
						||
| 
								 | 
							
								# the MIT License: https://www.opensource.org/licenses/mit-license.php
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								r"""Define an extension to the :mod:`sqlalchemy.ext.declarative` system
							 | 
						||
| 
								 | 
							
								which automatically generates mapped classes and relationships from a database
							 | 
						||
| 
								 | 
							
								schema, typically though not necessarily one which is reflected.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. versionadded:: 0.9.1 Added :mod:`sqlalchemy.ext.automap`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								It is hoped that the :class:`.AutomapBase` system provides a quick
							 | 
						||
| 
								 | 
							
								and modernized solution to the problem that the very famous
							 | 
						||
| 
								 | 
							
								`SQLSoup <https://sqlsoup.readthedocs.io/en/latest/>`_
							 | 
						||
| 
								 | 
							
								also tries to solve, that of generating a quick and rudimentary object
							 | 
						||
| 
								 | 
							
								model from an existing database on the fly.  By addressing the issue strictly
							 | 
						||
| 
								 | 
							
								at the mapper configuration level, and integrating fully with existing
							 | 
						||
| 
								 | 
							
								Declarative class techniques, :class:`.AutomapBase` seeks to provide
							 | 
						||
| 
								 | 
							
								a well-integrated approach to the issue of expediently auto-generating ad-hoc
							 | 
						||
| 
								 | 
							
								mappings.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Basic Use
							 | 
						||
| 
								 | 
							
								=========
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The simplest usage is to reflect an existing database into a new model.
							 | 
						||
| 
								 | 
							
								We create a new :class:`.AutomapBase` class in a similar manner as to how
							 | 
						||
| 
								 | 
							
								we create a declarative base class, using :func:`.automap_base`.
							 | 
						||
| 
								 | 
							
								We then call :meth:`.AutomapBase.prepare` on the resulting base class,
							 | 
						||
| 
								 | 
							
								asking it to reflect the schema and produce mappings::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.ext.automap import automap_base
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.orm import Session
							 | 
						||
| 
								 | 
							
								    from sqlalchemy import create_engine
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Base = automap_base()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # engine, suppose it has two tables 'user' and 'address' set up
							 | 
						||
| 
								 | 
							
								    engine = create_engine("sqlite:///mydatabase.db")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # reflect the tables
							 | 
						||
| 
								 | 
							
								    Base.prepare(engine, reflect=True)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # mapped classes are now created with names by default
							 | 
						||
| 
								 | 
							
								    # matching that of the table name.
							 | 
						||
| 
								 | 
							
								    User = Base.classes.user
							 | 
						||
| 
								 | 
							
								    Address = Base.classes.address
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    session = Session(engine)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # rudimentary relationships are produced
							 | 
						||
| 
								 | 
							
								    session.add(Address(email_address="foo@bar.com", user=User(name="foo")))
							 | 
						||
| 
								 | 
							
								    session.commit()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # collection-based relationships are by default named
							 | 
						||
| 
								 | 
							
								    # "<classname>_collection"
							 | 
						||
| 
								 | 
							
								    print (u1.address_collection)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Above, calling :meth:`.AutomapBase.prepare` while passing along the
							 | 
						||
| 
								 | 
							
								:paramref:`.AutomapBase.prepare.reflect` parameter indicates that the
							 | 
						||
| 
								 | 
							
								:meth:`_schema.MetaData.reflect`
							 | 
						||
| 
								 | 
							
								method will be called on this declarative base
							 | 
						||
| 
								 | 
							
								classes' :class:`_schema.MetaData` collection; then, each **viable**
							 | 
						||
| 
								 | 
							
								:class:`_schema.Table` within the :class:`_schema.MetaData`
							 | 
						||
| 
								 | 
							
								will get a new mapped class
							 | 
						||
| 
								 | 
							
								generated automatically.  The :class:`_schema.ForeignKeyConstraint`
							 | 
						||
| 
								 | 
							
								objects which
							 | 
						||
| 
								 | 
							
								link the various tables together will be used to produce new, bidirectional
							 | 
						||
| 
								 | 
							
								:func:`_orm.relationship` objects between classes.
							 | 
						||
| 
								 | 
							
								The classes and relationships
							 | 
						||
| 
								 | 
							
								follow along a default naming scheme that we can customize.  At this point,
							 | 
						||
| 
								 | 
							
								our basic mapping consisting of related ``User`` and ``Address`` classes is
							 | 
						||
| 
								 | 
							
								ready to use in the traditional way.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. note:: By **viable**, we mean that for a table to be mapped, it must
							 | 
						||
| 
								 | 
							
								   specify a primary key.  Additionally, if the table is detected as being
							 | 
						||
| 
								 | 
							
								   a pure association table between two other tables, it will not be directly
							 | 
						||
| 
								 | 
							
								   mapped and will instead be configured as a many-to-many table between
							 | 
						||
| 
								 | 
							
								   the mappings for the two referring tables.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Generating Mappings from an Existing MetaData
							 | 
						||
| 
								 | 
							
								=============================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								We can pass a pre-declared :class:`_schema.MetaData` object to
							 | 
						||
| 
								 | 
							
								:func:`.automap_base`.
							 | 
						||
| 
								 | 
							
								This object can be constructed in any way, including programmatically, from
							 | 
						||
| 
								 | 
							
								a serialized file, or from itself being reflected using
							 | 
						||
| 
								 | 
							
								:meth:`_schema.MetaData.reflect`.
							 | 
						||
| 
								 | 
							
								Below we illustrate a combination of reflection and
							 | 
						||
| 
								 | 
							
								explicit table declaration::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    from sqlalchemy import create_engine, MetaData, Table, Column, ForeignKey
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.ext.automap import automap_base
							 | 
						||
| 
								 | 
							
								    engine = create_engine("sqlite:///mydatabase.db")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # produce our own MetaData object
							 | 
						||
| 
								 | 
							
								    metadata = MetaData()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # we can reflect it ourselves from a database, using options
							 | 
						||
| 
								 | 
							
								    # such as 'only' to limit what tables we look at...
							 | 
						||
| 
								 | 
							
								    metadata.reflect(engine, only=['user', 'address'])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # ... or just define our own Table objects with it (or combine both)
							 | 
						||
| 
								 | 
							
								    Table('user_order', metadata,
							 | 
						||
| 
								 | 
							
								                    Column('id', Integer, primary_key=True),
							 | 
						||
| 
								 | 
							
								                    Column('user_id', ForeignKey('user.id'))
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # we can then produce a set of mappings from this MetaData.
							 | 
						||
| 
								 | 
							
								    Base = automap_base(metadata=metadata)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # calling prepare() just sets up mapped classes and relationships.
							 | 
						||
| 
								 | 
							
								    Base.prepare()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # mapped classes are ready
							 | 
						||
| 
								 | 
							
								    User, Address, Order = Base.classes.user, Base.classes.address,\
							 | 
						||
| 
								 | 
							
								        Base.classes.user_order
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Specifying Classes Explicitly
							 | 
						||
| 
								 | 
							
								=============================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The :mod:`.sqlalchemy.ext.automap` extension allows classes to be defined
							 | 
						||
| 
								 | 
							
								explicitly, in a way similar to that of the :class:`.DeferredReflection` class.
							 | 
						||
| 
								 | 
							
								Classes that extend from :class:`.AutomapBase` act like regular declarative
							 | 
						||
| 
								 | 
							
								classes, but are not immediately mapped after their construction, and are
							 | 
						||
| 
								 | 
							
								instead mapped when we call :meth:`.AutomapBase.prepare`.  The
							 | 
						||
| 
								 | 
							
								:meth:`.AutomapBase.prepare` method will make use of the classes we've
							 | 
						||
| 
								 | 
							
								established based on the table name we use.  If our schema contains tables
							 | 
						||
| 
								 | 
							
								``user`` and ``address``, we can define one or both of the classes to be used::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.ext.automap import automap_base
							 | 
						||
| 
								 | 
							
								    from sqlalchemy import create_engine
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # automap base
							 | 
						||
| 
								 | 
							
								    Base = automap_base()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # pre-declare User for the 'user' table
							 | 
						||
| 
								 | 
							
								    class User(Base):
							 | 
						||
| 
								 | 
							
								        __tablename__ = 'user'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # override schema elements like Columns
							 | 
						||
| 
								 | 
							
								        user_name = Column('name', String)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # override relationships too, if desired.
							 | 
						||
| 
								 | 
							
								        # we must use the same name that automap would use for the
							 | 
						||
| 
								 | 
							
								        # relationship, and also must refer to the class name that automap will
							 | 
						||
| 
								 | 
							
								        # generate for "address"
							 | 
						||
| 
								 | 
							
								        address_collection = relationship("address", collection_class=set)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # reflect
							 | 
						||
| 
								 | 
							
								    engine = create_engine("sqlite:///mydatabase.db")
							 | 
						||
| 
								 | 
							
								    Base.prepare(engine, reflect=True)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # we still have Address generated from the tablename "address",
							 | 
						||
| 
								 | 
							
								    # but User is the same as Base.classes.User now
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Address = Base.classes.address
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    u1 = session.query(User).first()
							 | 
						||
| 
								 | 
							
								    print (u1.address_collection)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # the backref is still there:
							 | 
						||
| 
								 | 
							
								    a1 = session.query(Address).first()
							 | 
						||
| 
								 | 
							
								    print (a1.user)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Above, one of the more intricate details is that we illustrated overriding
							 | 
						||
| 
								 | 
							
								one of the :func:`_orm.relationship` objects that automap would have created.
							 | 
						||
| 
								 | 
							
								To do this, we needed to make sure the names match up with what automap
							 | 
						||
| 
								 | 
							
								would normally generate, in that the relationship name would be
							 | 
						||
| 
								 | 
							
								``User.address_collection`` and the name of the class referred to, from
							 | 
						||
| 
								 | 
							
								automap's perspective, is called ``address``, even though we are referring to
							 | 
						||
| 
								 | 
							
								it as ``Address`` within our usage of this class.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Overriding Naming Schemes
							 | 
						||
| 
								 | 
							
								=========================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								:mod:`.sqlalchemy.ext.automap` is tasked with producing mapped classes and
							 | 
						||
| 
								 | 
							
								relationship names based on a schema, which means it has decision points in how
							 | 
						||
| 
								 | 
							
								these names are determined.  These three decision points are provided using
							 | 
						||
| 
								 | 
							
								functions which can be passed to the :meth:`.AutomapBase.prepare` method, and
							 | 
						||
| 
								 | 
							
								are known as :func:`.classname_for_table`,
							 | 
						||
| 
								 | 
							
								:func:`.name_for_scalar_relationship`,
							 | 
						||
| 
								 | 
							
								and :func:`.name_for_collection_relationship`.  Any or all of these
							 | 
						||
| 
								 | 
							
								functions are provided as in the example below, where we use a "camel case"
							 | 
						||
| 
								 | 
							
								scheme for class names and a "pluralizer" for collection names using the
							 | 
						||
| 
								 | 
							
								`Inflect <https://pypi.org/project/inflect>`_ package::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    import re
							 | 
						||
| 
								 | 
							
								    import inflect
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def camelize_classname(base, tablename, table):
							 | 
						||
| 
								 | 
							
								        "Produce a 'camelized' class name, e.g. "
							 | 
						||
| 
								 | 
							
								        "'words_and_underscores' -> 'WordsAndUnderscores'"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return str(tablename[0].upper() + \
							 | 
						||
| 
								 | 
							
								                re.sub(r'_([a-z])', lambda m: m.group(1).upper(), tablename[1:]))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _pluralizer = inflect.engine()
							 | 
						||
| 
								 | 
							
								    def pluralize_collection(base, local_cls, referred_cls, constraint):
							 | 
						||
| 
								 | 
							
								        "Produce an 'uncamelized', 'pluralized' class name, e.g. "
							 | 
						||
| 
								 | 
							
								        "'SomeTerm' -> 'some_terms'"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        referred_name = referred_cls.__name__
							 | 
						||
| 
								 | 
							
								        uncamelized = re.sub(r'[A-Z]',
							 | 
						||
| 
								 | 
							
								                             lambda m: "_%s" % m.group(0).lower(),
							 | 
						||
| 
								 | 
							
								                             referred_name)[1:]
							 | 
						||
| 
								 | 
							
								        pluralized = _pluralizer.plural(uncamelized)
							 | 
						||
| 
								 | 
							
								        return pluralized
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.ext.automap import automap_base
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Base = automap_base()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    engine = create_engine("sqlite:///mydatabase.db")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Base.prepare(engine, reflect=True,
							 | 
						||
| 
								 | 
							
								                classname_for_table=camelize_classname,
							 | 
						||
| 
								 | 
							
								                name_for_collection_relationship=pluralize_collection
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								From the above mapping, we would now have classes ``User`` and ``Address``,
							 | 
						||
| 
								 | 
							
								where the collection from ``User`` to ``Address`` is called
							 | 
						||
| 
								 | 
							
								``User.addresses``::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    User, Address = Base.classes.User, Base.classes.Address
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    u1 = User(addresses=[Address(email="foo@bar.com")])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Relationship Detection
							 | 
						||
| 
								 | 
							
								======================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The vast majority of what automap accomplishes is the generation of
							 | 
						||
| 
								 | 
							
								:func:`_orm.relationship` structures based on foreign keys.  The mechanism
							 | 
						||
| 
								 | 
							
								by which this works for many-to-one and one-to-many relationships is as
							 | 
						||
| 
								 | 
							
								follows:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								1. A given :class:`_schema.Table`, known to be mapped to a particular class,
							 | 
						||
| 
								 | 
							
								   is examined for :class:`_schema.ForeignKeyConstraint` objects.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								2. From each :class:`_schema.ForeignKeyConstraint`, the remote
							 | 
						||
| 
								 | 
							
								   :class:`_schema.Table`
							 | 
						||
| 
								 | 
							
								   object present is matched up to the class to which it is to be mapped,
							 | 
						||
| 
								 | 
							
								   if any, else it is skipped.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								3. As the :class:`_schema.ForeignKeyConstraint`
							 | 
						||
| 
								 | 
							
								   we are examining corresponds to a
							 | 
						||
| 
								 | 
							
								   reference from the immediate mapped class,  the relationship will be set up
							 | 
						||
| 
								 | 
							
								   as a many-to-one referring to the referred class; a corresponding
							 | 
						||
| 
								 | 
							
								   one-to-many backref will be created on the referred class referring
							 | 
						||
| 
								 | 
							
								   to this class.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								4. If any of the columns that are part of the
							 | 
						||
| 
								 | 
							
								   :class:`_schema.ForeignKeyConstraint`
							 | 
						||
| 
								 | 
							
								   are not nullable (e.g. ``nullable=False``), a
							 | 
						||
| 
								 | 
							
								   :paramref:`_orm.relationship.cascade` keyword argument
							 | 
						||
| 
								 | 
							
								   of ``all, delete-orphan`` will be added to the keyword arguments to
							 | 
						||
| 
								 | 
							
								   be passed to the relationship or backref.  If the
							 | 
						||
| 
								 | 
							
								   :class:`_schema.ForeignKeyConstraint` reports that
							 | 
						||
| 
								 | 
							
								   :paramref:`_schema.ForeignKeyConstraint.ondelete`
							 | 
						||
| 
								 | 
							
								   is set to ``CASCADE`` for a not null or ``SET NULL`` for a nullable
							 | 
						||
| 
								 | 
							
								   set of columns, the option :paramref:`_orm.relationship.passive_deletes`
							 | 
						||
| 
								 | 
							
								   flag is set to ``True`` in the set of relationship keyword arguments.
							 | 
						||
| 
								 | 
							
								   Note that not all backends support reflection of ON DELETE.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								   .. versionadded:: 1.0.0 - automap will detect non-nullable foreign key
							 | 
						||
| 
								 | 
							
								      constraints when producing a one-to-many relationship and establish
							 | 
						||
| 
								 | 
							
								      a default cascade of ``all, delete-orphan`` if so; additionally,
							 | 
						||
| 
								 | 
							
								      if the constraint specifies
							 | 
						||
| 
								 | 
							
								      :paramref:`_schema.ForeignKeyConstraint.ondelete`
							 | 
						||
| 
								 | 
							
								      of ``CASCADE`` for non-nullable or ``SET NULL`` for nullable columns,
							 | 
						||
| 
								 | 
							
								      the ``passive_deletes=True`` option is also added.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								5. The names of the relationships are determined using the
							 | 
						||
| 
								 | 
							
								   :paramref:`.AutomapBase.prepare.name_for_scalar_relationship` and
							 | 
						||
| 
								 | 
							
								   :paramref:`.AutomapBase.prepare.name_for_collection_relationship`
							 | 
						||
| 
								 | 
							
								   callable functions.  It is important to note that the default relationship
							 | 
						||
| 
								 | 
							
								   naming derives the name from the **the actual class name**.  If you've
							 | 
						||
| 
								 | 
							
								   given a particular class an explicit name by declaring it, or specified an
							 | 
						||
| 
								 | 
							
								   alternate class naming scheme, that's the name from which the relationship
							 | 
						||
| 
								 | 
							
								   name will be derived.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								6. The classes are inspected for an existing mapped property matching these
							 | 
						||
| 
								 | 
							
								   names.  If one is detected on one side, but none on the other side,
							 | 
						||
| 
								 | 
							
								   :class:`.AutomapBase` attempts to create a relationship on the missing side,
							 | 
						||
| 
								 | 
							
								   then uses the :paramref:`_orm.relationship.back_populates`
							 | 
						||
| 
								 | 
							
								   parameter in order to
							 | 
						||
| 
								 | 
							
								   point the new relationship to the other side.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								7. In the usual case where no relationship is on either side,
							 | 
						||
| 
								 | 
							
								   :meth:`.AutomapBase.prepare` produces a :func:`_orm.relationship` on the
							 | 
						||
| 
								 | 
							
								   "many-to-one" side and matches it to the other using the
							 | 
						||
| 
								 | 
							
								   :paramref:`_orm.relationship.backref` parameter.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								8. Production of the :func:`_orm.relationship` and optionally the
							 | 
						||
| 
								 | 
							
								   :func:`.backref`
							 | 
						||
| 
								 | 
							
								   is handed off to the :paramref:`.AutomapBase.prepare.generate_relationship`
							 | 
						||
| 
								 | 
							
								   function, which can be supplied by the end-user in order to augment
							 | 
						||
| 
								 | 
							
								   the arguments passed to :func:`_orm.relationship` or :func:`.backref` or to
							 | 
						||
| 
								 | 
							
								   make use of custom implementations of these functions.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Custom Relationship Arguments
							 | 
						||
| 
								 | 
							
								-----------------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The :paramref:`.AutomapBase.prepare.generate_relationship` hook can be used
							 | 
						||
| 
								 | 
							
								to add parameters to relationships.  For most cases, we can make use of the
							 | 
						||
| 
								 | 
							
								existing :func:`.automap.generate_relationship` function to return
							 | 
						||
| 
								 | 
							
								the object, after augmenting the given keyword dictionary with our own
							 | 
						||
| 
								 | 
							
								arguments.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Below is an illustration of how to send
							 | 
						||
| 
								 | 
							
								:paramref:`_orm.relationship.cascade` and
							 | 
						||
| 
								 | 
							
								:paramref:`_orm.relationship.passive_deletes`
							 | 
						||
| 
								 | 
							
								options along to all one-to-many relationships::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.ext.automap import generate_relationship
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _gen_relationship(base, direction, return_fn,
							 | 
						||
| 
								 | 
							
								                                    attrname, local_cls, referred_cls, **kw):
							 | 
						||
| 
								 | 
							
								        if direction is interfaces.ONETOMANY:
							 | 
						||
| 
								 | 
							
								            kw['cascade'] = 'all, delete-orphan'
							 | 
						||
| 
								 | 
							
								            kw['passive_deletes'] = True
							 | 
						||
| 
								 | 
							
								        # make use of the built-in function to actually return
							 | 
						||
| 
								 | 
							
								        # the result.
							 | 
						||
| 
								 | 
							
								        return generate_relationship(base, direction, return_fn,
							 | 
						||
| 
								 | 
							
								                                     attrname, local_cls, referred_cls, **kw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.ext.automap import automap_base
							 | 
						||
| 
								 | 
							
								    from sqlalchemy import create_engine
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # automap base
							 | 
						||
| 
								 | 
							
								    Base = automap_base()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    engine = create_engine("sqlite:///mydatabase.db")
							 | 
						||
| 
								 | 
							
								    Base.prepare(engine, reflect=True,
							 | 
						||
| 
								 | 
							
								                generate_relationship=_gen_relationship)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Many-to-Many relationships
							 | 
						||
| 
								 | 
							
								--------------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								:mod:`.sqlalchemy.ext.automap` will generate many-to-many relationships, e.g.
							 | 
						||
| 
								 | 
							
								those which contain a ``secondary`` argument.  The process for producing these
							 | 
						||
| 
								 | 
							
								is as follows:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								1. A given :class:`_schema.Table` is examined for
							 | 
						||
| 
								 | 
							
								   :class:`_schema.ForeignKeyConstraint`
							 | 
						||
| 
								 | 
							
								   objects, before any mapped class has been assigned to it.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								2. If the table contains two and exactly two
							 | 
						||
| 
								 | 
							
								   :class:`_schema.ForeignKeyConstraint`
							 | 
						||
| 
								 | 
							
								   objects, and all columns within this table are members of these two
							 | 
						||
| 
								 | 
							
								   :class:`_schema.ForeignKeyConstraint` objects, the table is assumed to be a
							 | 
						||
| 
								 | 
							
								   "secondary" table, and will **not be mapped directly**.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								3. The two (or one, for self-referential) external tables to which the
							 | 
						||
| 
								 | 
							
								   :class:`_schema.Table`
							 | 
						||
| 
								 | 
							
								   refers to are matched to the classes to which they will be
							 | 
						||
| 
								 | 
							
								   mapped, if any.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								4. If mapped classes for both sides are located, a many-to-many bi-directional
							 | 
						||
| 
								 | 
							
								   :func:`_orm.relationship` / :func:`.backref`
							 | 
						||
| 
								 | 
							
								   pair is created between the two
							 | 
						||
| 
								 | 
							
								   classes.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								5. The override logic for many-to-many works the same as that of one-to-many/
							 | 
						||
| 
								 | 
							
								   many-to-one; the :func:`.generate_relationship` function is called upon
							 | 
						||
| 
								 | 
							
								   to generate the structures and existing attributes will be maintained.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Relationships with Inheritance
							 | 
						||
| 
								 | 
							
								------------------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								:mod:`.sqlalchemy.ext.automap` will not generate any relationships between
							 | 
						||
| 
								 | 
							
								two classes that are in an inheritance relationship.   That is, with two
							 | 
						||
| 
								 | 
							
								classes given as follows::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class Employee(Base):
							 | 
						||
| 
								 | 
							
								        __tablename__ = 'employee'
							 | 
						||
| 
								 | 
							
								        id = Column(Integer, primary_key=True)
							 | 
						||
| 
								 | 
							
								        type = Column(String(50))
							 | 
						||
| 
								 | 
							
								        __mapper_args__ = {
							 | 
						||
| 
								 | 
							
								             'polymorphic_identity':'employee', 'polymorphic_on': type
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class Engineer(Employee):
							 | 
						||
| 
								 | 
							
								        __tablename__ = 'engineer'
							 | 
						||
| 
								 | 
							
								        id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
							 | 
						||
| 
								 | 
							
								        __mapper_args__ = {
							 | 
						||
| 
								 | 
							
								            'polymorphic_identity':'engineer',
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The foreign key from ``Engineer`` to ``Employee`` is used not for a
							 | 
						||
| 
								 | 
							
								relationship, but to establish joined inheritance between the two classes.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Note that this means automap will not generate *any* relationships
							 | 
						||
| 
								 | 
							
								for foreign keys that link from a subclass to a superclass.  If a mapping
							 | 
						||
| 
								 | 
							
								has actual relationships from subclass to superclass as well, those
							 | 
						||
| 
								 | 
							
								need to be explicit.  Below, as we have two separate foreign keys
							 | 
						||
| 
								 | 
							
								from ``Engineer`` to ``Employee``, we need to set up both the relationship
							 | 
						||
| 
								 | 
							
								we want as well as the ``inherit_condition``, as these are not things
							 | 
						||
| 
								 | 
							
								SQLAlchemy can guess::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class Employee(Base):
							 | 
						||
| 
								 | 
							
								        __tablename__ = 'employee'
							 | 
						||
| 
								 | 
							
								        id = Column(Integer, primary_key=True)
							 | 
						||
| 
								 | 
							
								        type = Column(String(50))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        __mapper_args__ = {
							 | 
						||
| 
								 | 
							
								            'polymorphic_identity':'employee', 'polymorphic_on':type
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class Engineer(Employee):
							 | 
						||
| 
								 | 
							
								        __tablename__ = 'engineer'
							 | 
						||
| 
								 | 
							
								        id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
							 | 
						||
| 
								 | 
							
								        favorite_employee_id = Column(Integer, ForeignKey('employee.id'))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        favorite_employee = relationship(Employee,
							 | 
						||
| 
								 | 
							
								                                         foreign_keys=favorite_employee_id)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        __mapper_args__ = {
							 | 
						||
| 
								 | 
							
								            'polymorphic_identity':'engineer',
							 | 
						||
| 
								 | 
							
								            'inherit_condition': id == Employee.id
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Handling Simple Naming Conflicts
							 | 
						||
| 
								 | 
							
								--------------------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								In the case of naming conflicts during mapping, override any of
							 | 
						||
| 
								 | 
							
								:func:`.classname_for_table`, :func:`.name_for_scalar_relationship`,
							 | 
						||
| 
								 | 
							
								and :func:`.name_for_collection_relationship` as needed.  For example, if
							 | 
						||
| 
								 | 
							
								automap is attempting to name a many-to-one relationship the same as an
							 | 
						||
| 
								 | 
							
								existing column, an alternate convention can be conditionally selected.  Given
							 | 
						||
| 
								 | 
							
								a schema:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. sourcecode:: sql
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    CREATE TABLE table_a (
							 | 
						||
| 
								 | 
							
								        id INTEGER PRIMARY KEY
							 | 
						||
| 
								 | 
							
								    );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    CREATE TABLE table_b (
							 | 
						||
| 
								 | 
							
								        id INTEGER PRIMARY KEY,
							 | 
						||
| 
								 | 
							
								        table_a INTEGER,
							 | 
						||
| 
								 | 
							
								        FOREIGN KEY(table_a) REFERENCES table_a(id)
							 | 
						||
| 
								 | 
							
								    );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The above schema will first automap the ``table_a`` table as a class named
							 | 
						||
| 
								 | 
							
								``table_a``; it will then automap a relationship onto the class for ``table_b``
							 | 
						||
| 
								 | 
							
								with the same name as this related class, e.g. ``table_a``.  This
							 | 
						||
| 
								 | 
							
								relationship name conflicts with the mapping column ``table_b.table_a``,
							 | 
						||
| 
								 | 
							
								and will emit an error on mapping.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								We can resolve this conflict by using an underscore as follows::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def name_for_scalar_relationship(base, local_cls, referred_cls, constraint):
							 | 
						||
| 
								 | 
							
								        name = referred_cls.__name__.lower()
							 | 
						||
| 
								 | 
							
								        local_table = local_cls.__table__
							 | 
						||
| 
								 | 
							
								        if name in local_table.columns:
							 | 
						||
| 
								 | 
							
								            newname = name + "_"
							 | 
						||
| 
								 | 
							
								            warnings.warn(
							 | 
						||
| 
								 | 
							
								                "Already detected name %s present.  using %s" %
							 | 
						||
| 
								 | 
							
								                (name, newname))
							 | 
						||
| 
								 | 
							
								            return newname
							 | 
						||
| 
								 | 
							
								        return name
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Base.prepare(engine, reflect=True,
							 | 
						||
| 
								 | 
							
								        name_for_scalar_relationship=name_for_scalar_relationship)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Alternatively, we can change the name on the column side.   The columns
							 | 
						||
| 
								 | 
							
								that are mapped can be modified using the technique described at
							 | 
						||
| 
								 | 
							
								:ref:`mapper_column_distinct_names`, by assigning the column explicitly
							 | 
						||
| 
								 | 
							
								to a new name::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Base = automap_base()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class TableB(Base):
							 | 
						||
| 
								 | 
							
								        __tablename__ = 'table_b'
							 | 
						||
| 
								 | 
							
								        _table_a = Column('table_a', ForeignKey('table_a.id'))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Base.prepare(engine, reflect=True)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Using Automap with Explicit Declarations
							 | 
						||
| 
								 | 
							
								========================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								As noted previously, automap has no dependency on reflection, and can make
							 | 
						||
| 
								 | 
							
								use of any collection of :class:`_schema.Table` objects within a
							 | 
						||
| 
								 | 
							
								:class:`_schema.MetaData`
							 | 
						||
| 
								 | 
							
								collection.  From this, it follows that automap can also be used
							 | 
						||
| 
								 | 
							
								generate missing relationships given an otherwise complete model that fully
							 | 
						||
| 
								 | 
							
								defines table metadata::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.ext.automap import automap_base
							 | 
						||
| 
								 | 
							
								    from sqlalchemy import Column, Integer, String, ForeignKey
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Base = automap_base()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class User(Base):
							 | 
						||
| 
								 | 
							
								        __tablename__ = 'user'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        id = Column(Integer, primary_key=True)
							 | 
						||
| 
								 | 
							
								        name = Column(String)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class Address(Base):
							 | 
						||
| 
								 | 
							
								        __tablename__ = 'address'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        id = Column(Integer, primary_key=True)
							 | 
						||
| 
								 | 
							
								        email = Column(String)
							 | 
						||
| 
								 | 
							
								        user_id = Column(ForeignKey('user.id'))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # produce relationships
							 | 
						||
| 
								 | 
							
								    Base.prepare()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # mapping is complete, with "address_collection" and
							 | 
						||
| 
								 | 
							
								    # "user" relationships
							 | 
						||
| 
								 | 
							
								    a1 = Address(email='u1')
							 | 
						||
| 
								 | 
							
								    a2 = Address(email='u2')
							 | 
						||
| 
								 | 
							
								    u1 = User(address_collection=[a1, a2])
							 | 
						||
| 
								 | 
							
								    assert a1.user is u1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Above, given mostly complete ``User`` and ``Address`` mappings, the
							 | 
						||
| 
								 | 
							
								:class:`_schema.ForeignKey` which we defined on ``Address.user_id`` allowed a
							 | 
						||
| 
								 | 
							
								bidirectional relationship pair ``Address.user`` and
							 | 
						||
| 
								 | 
							
								``User.address_collection`` to be generated on the mapped classes.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Note that when subclassing :class:`.AutomapBase`,
							 | 
						||
| 
								 | 
							
								the :meth:`.AutomapBase.prepare` method is required; if not called, the classes
							 | 
						||
| 
								 | 
							
								we've declared are in an un-mapped state.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. _automap_intercepting_columns:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Intercepting Column Definitions
							 | 
						||
| 
								 | 
							
								===============================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The :class:`_schema.MetaData` and :class:`_schema.Table` objects support an
							 | 
						||
| 
								 | 
							
								event hook :meth:`_events.DDLEvents.column_reflect` that may be used to intercept
							 | 
						||
| 
								 | 
							
								the information reflected about a database column before the :class:`_schema.Column`
							 | 
						||
| 
								 | 
							
								object is constructed.   For example if we wanted to map columns using a
							 | 
						||
| 
								 | 
							
								naming convention such as ``"attr_<columnname>"``, the event could
							 | 
						||
| 
								 | 
							
								be applied as::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @event.listens_for(Base.metadata, "column_reflect")
							 | 
						||
| 
								 | 
							
								    def column_reflect(inspector, table, column_info):
							 | 
						||
| 
								 | 
							
								        # set column.key = "attr_<lower_case_name>"
							 | 
						||
| 
								 | 
							
								        column_info['key'] = "attr_%s" % column_info['name'].lower()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # run reflection
							 | 
						||
| 
								 | 
							
								    Base.prepare(engine, reflect=True)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. versionadded:: 1.4.0b2 the :meth:`_events.DDLEvents.column_reflect` event
							 | 
						||
| 
								 | 
							
								   may be applied to a :class:`_schema.MetaData` object.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      :meth:`_events.DDLEvents.column_reflect`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      :ref:`mapper_automated_reflection_schemes` - in the ORM mapping documentation
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								"""  # noqa
							 | 
						||
| 
								 | 
							
								from .. import util
							 | 
						||
| 
								 | 
							
								from ..orm import backref
							 | 
						||
| 
								 | 
							
								from ..orm import declarative_base as _declarative_base
							 | 
						||
| 
								 | 
							
								from ..orm import exc as orm_exc
							 | 
						||
| 
								 | 
							
								from ..orm import interfaces
							 | 
						||
| 
								 | 
							
								from ..orm import relationship
							 | 
						||
| 
								 | 
							
								from ..orm.decl_base import _DeferredMapperConfig
							 | 
						||
| 
								 | 
							
								from ..orm.mapper import _CONFIGURE_MUTEX
							 | 
						||
| 
								 | 
							
								from ..schema import ForeignKeyConstraint
							 | 
						||
| 
								 | 
							
								from ..sql import and_
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def classname_for_table(base, tablename, table):
							 | 
						||
| 
								 | 
							
								    """Return the class name that should be used, given the name
							 | 
						||
| 
								 | 
							
								    of a table.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The default implementation is::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return str(tablename)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Alternate implementations can be specified using the
							 | 
						||
| 
								 | 
							
								    :paramref:`.AutomapBase.prepare.classname_for_table`
							 | 
						||
| 
								 | 
							
								    parameter.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param base: the :class:`.AutomapBase` class doing the prepare.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param tablename: string name of the :class:`_schema.Table`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param table: the :class:`_schema.Table` object itself.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :return: a string class name.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     .. note::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        In Python 2, the string used for the class name **must** be a
							 | 
						||
| 
								 | 
							
								        non-Unicode object, e.g. a ``str()`` object.  The ``.name`` attribute
							 | 
						||
| 
								 | 
							
								        of :class:`_schema.Table` is typically a Python unicode subclass,
							 | 
						||
| 
								 | 
							
								        so the
							 | 
						||
| 
								 | 
							
								        ``str()`` function should be applied to this name, after accounting for
							 | 
						||
| 
								 | 
							
								        any non-ASCII characters.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    return str(tablename)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def name_for_scalar_relationship(base, local_cls, referred_cls, constraint):
							 | 
						||
| 
								 | 
							
								    """Return the attribute name that should be used to refer from one
							 | 
						||
| 
								 | 
							
								    class to another, for a scalar object reference.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The default implementation is::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return referred_cls.__name__.lower()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Alternate implementations can be specified using the
							 | 
						||
| 
								 | 
							
								    :paramref:`.AutomapBase.prepare.name_for_scalar_relationship`
							 | 
						||
| 
								 | 
							
								    parameter.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param base: the :class:`.AutomapBase` class doing the prepare.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param local_cls: the class to be mapped on the local side.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param referred_cls: the class to be mapped on the referring side.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param constraint: the :class:`_schema.ForeignKeyConstraint` that is being
							 | 
						||
| 
								 | 
							
								     inspected to produce this relationship.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    return referred_cls.__name__.lower()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def name_for_collection_relationship(
							 | 
						||
| 
								 | 
							
								    base, local_cls, referred_cls, constraint
							 | 
						||
| 
								 | 
							
								):
							 | 
						||
| 
								 | 
							
								    """Return the attribute name that should be used to refer from one
							 | 
						||
| 
								 | 
							
								    class to another, for a collection reference.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The default implementation is::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return referred_cls.__name__.lower() + "_collection"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Alternate implementations
							 | 
						||
| 
								 | 
							
								    can be specified using the
							 | 
						||
| 
								 | 
							
								    :paramref:`.AutomapBase.prepare.name_for_collection_relationship`
							 | 
						||
| 
								 | 
							
								    parameter.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param base: the :class:`.AutomapBase` class doing the prepare.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param local_cls: the class to be mapped on the local side.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param referred_cls: the class to be mapped on the referring side.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param constraint: the :class:`_schema.ForeignKeyConstraint` that is being
							 | 
						||
| 
								 | 
							
								     inspected to produce this relationship.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    return referred_cls.__name__.lower() + "_collection"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def generate_relationship(
							 | 
						||
| 
								 | 
							
								    base, direction, return_fn, attrname, local_cls, referred_cls, **kw
							 | 
						||
| 
								 | 
							
								):
							 | 
						||
| 
								 | 
							
								    r"""Generate a :func:`_orm.relationship` or :func:`.backref`
							 | 
						||
| 
								 | 
							
								    on behalf of two
							 | 
						||
| 
								 | 
							
								    mapped classes.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    An alternate implementation of this function can be specified using the
							 | 
						||
| 
								 | 
							
								    :paramref:`.AutomapBase.prepare.generate_relationship` parameter.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The default implementation of this function is as follows::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if return_fn is backref:
							 | 
						||
| 
								 | 
							
								            return return_fn(attrname, **kw)
							 | 
						||
| 
								 | 
							
								        elif return_fn is relationship:
							 | 
						||
| 
								 | 
							
								            return return_fn(referred_cls, **kw)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            raise TypeError("Unknown relationship function: %s" % return_fn)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param base: the :class:`.AutomapBase` class doing the prepare.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param direction: indicate the "direction" of the relationship; this will
							 | 
						||
| 
								 | 
							
								     be one of :data:`.ONETOMANY`, :data:`.MANYTOONE`, :data:`.MANYTOMANY`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param return_fn: the function that is used by default to create the
							 | 
						||
| 
								 | 
							
								     relationship.  This will be either :func:`_orm.relationship` or
							 | 
						||
| 
								 | 
							
								     :func:`.backref`.  The :func:`.backref` function's result will be used to
							 | 
						||
| 
								 | 
							
								     produce a new :func:`_orm.relationship` in a second step,
							 | 
						||
| 
								 | 
							
								     so it is critical
							 | 
						||
| 
								 | 
							
								     that user-defined implementations correctly differentiate between the two
							 | 
						||
| 
								 | 
							
								     functions, if a custom relationship function is being used.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param attrname: the attribute name to which this relationship is being
							 | 
						||
| 
								 | 
							
								     assigned. If the value of :paramref:`.generate_relationship.return_fn` is
							 | 
						||
| 
								 | 
							
								     the :func:`.backref` function, then this name is the name that is being
							 | 
						||
| 
								 | 
							
								     assigned to the backref.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param local_cls: the "local" class to which this relationship or backref
							 | 
						||
| 
								 | 
							
								     will be locally present.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param referred_cls: the "referred" class to which the relationship or
							 | 
						||
| 
								 | 
							
								     backref refers to.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param \**kw: all additional keyword arguments are passed along to the
							 | 
						||
| 
								 | 
							
								     function.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :return: a :func:`_orm.relationship` or :func:`.backref` construct,
							 | 
						||
| 
								 | 
							
								     as dictated
							 | 
						||
| 
								 | 
							
								     by the :paramref:`.generate_relationship.return_fn` parameter.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    if return_fn is backref:
							 | 
						||
| 
								 | 
							
								        return return_fn(attrname, **kw)
							 | 
						||
| 
								 | 
							
								    elif return_fn is relationship:
							 | 
						||
| 
								 | 
							
								        return return_fn(referred_cls, **kw)
							 | 
						||
| 
								 | 
							
								    else:
							 | 
						||
| 
								 | 
							
								        raise TypeError("Unknown relationship function: %s" % return_fn)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class AutomapBase(object):
							 | 
						||
| 
								 | 
							
								    """Base class for an "automap" schema.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The :class:`.AutomapBase` class can be compared to the "declarative base"
							 | 
						||
| 
								 | 
							
								    class that is produced by the :func:`.declarative.declarative_base`
							 | 
						||
| 
								 | 
							
								    function.  In practice, the :class:`.AutomapBase` class is always used
							 | 
						||
| 
								 | 
							
								    as a mixin along with an actual declarative base.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    A new subclassable :class:`.AutomapBase` is typically instantiated
							 | 
						||
| 
								 | 
							
								    using the :func:`.automap_base` function.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`automap_toplevel`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    __abstract__ = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    classes = None
							 | 
						||
| 
								 | 
							
								    """An instance of :class:`.util.Properties` containing classes.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This object behaves much like the ``.c`` collection on a table.  Classes
							 | 
						||
| 
								 | 
							
								    are present under the name they were given, e.g.::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Base = automap_base()
							 | 
						||
| 
								 | 
							
								        Base.prepare(engine=some_engine, reflect=True)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        User, Address = Base.classes.User, Base.classes.Address
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    @util.deprecated_params(
							 | 
						||
| 
								 | 
							
								        engine=(
							 | 
						||
| 
								 | 
							
								            "2.0",
							 | 
						||
| 
								 | 
							
								            "The :paramref:`_automap.AutomapBase.prepare.engine` parameter "
							 | 
						||
| 
								 | 
							
								            "is deprecated and will be removed in a future release.  "
							 | 
						||
| 
								 | 
							
								            "Please use the "
							 | 
						||
| 
								 | 
							
								            ":paramref:`_automap.AutomapBase.prepare.autoload_with` "
							 | 
						||
| 
								 | 
							
								            "parameter.",
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        reflect=(
							 | 
						||
| 
								 | 
							
								            "2.0",
							 | 
						||
| 
								 | 
							
								            "The :paramref:`_automap.AutomapBase.prepare.reflect` "
							 | 
						||
| 
								 | 
							
								            "parameter is deprecated and will be removed in a future "
							 | 
						||
| 
								 | 
							
								            "release.  Reflection is enabled when "
							 | 
						||
| 
								 | 
							
								            ":paramref:`_automap.AutomapBase.prepare.autoload_with` "
							 | 
						||
| 
								 | 
							
								            "is passed.",
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								    def prepare(
							 | 
						||
| 
								 | 
							
								        cls,
							 | 
						||
| 
								 | 
							
								        autoload_with=None,
							 | 
						||
| 
								 | 
							
								        engine=None,
							 | 
						||
| 
								 | 
							
								        reflect=False,
							 | 
						||
| 
								 | 
							
								        schema=None,
							 | 
						||
| 
								 | 
							
								        classname_for_table=None,
							 | 
						||
| 
								 | 
							
								        collection_class=None,
							 | 
						||
| 
								 | 
							
								        name_for_scalar_relationship=None,
							 | 
						||
| 
								 | 
							
								        name_for_collection_relationship=None,
							 | 
						||
| 
								 | 
							
								        generate_relationship=None,
							 | 
						||
| 
								 | 
							
								        reflection_options=util.EMPTY_DICT,
							 | 
						||
| 
								 | 
							
								    ):
							 | 
						||
| 
								 | 
							
								        """Extract mapped classes and relationships from the
							 | 
						||
| 
								 | 
							
								        :class:`_schema.MetaData` and
							 | 
						||
| 
								 | 
							
								        perform mappings.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :param engine: an :class:`_engine.Engine` or
							 | 
						||
| 
								 | 
							
								         :class:`_engine.Connection` with which
							 | 
						||
| 
								 | 
							
								         to perform schema reflection, if specified.
							 | 
						||
| 
								 | 
							
								         If the :paramref:`.AutomapBase.prepare.reflect` argument is False,
							 | 
						||
| 
								 | 
							
								         this object is not used.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :param reflect: if True, the :meth:`_schema.MetaData.reflect`
							 | 
						||
| 
								 | 
							
								         method is called
							 | 
						||
| 
								 | 
							
								         on the :class:`_schema.MetaData` associated with this
							 | 
						||
| 
								 | 
							
								         :class:`.AutomapBase`.
							 | 
						||
| 
								 | 
							
								         The :class:`_engine.Engine` passed via
							 | 
						||
| 
								 | 
							
								         :paramref:`.AutomapBase.prepare.engine` will be used to perform the
							 | 
						||
| 
								 | 
							
								         reflection if present; else, the :class:`_schema.MetaData`
							 | 
						||
| 
								 | 
							
								         should already be
							 | 
						||
| 
								 | 
							
								         bound to some engine else the operation will fail.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :param classname_for_table: callable function which will be used to
							 | 
						||
| 
								 | 
							
								         produce new class names, given a table name.  Defaults to
							 | 
						||
| 
								 | 
							
								         :func:`.classname_for_table`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :param name_for_scalar_relationship: callable function which will be
							 | 
						||
| 
								 | 
							
								         used to produce relationship names for scalar relationships.  Defaults
							 | 
						||
| 
								 | 
							
								         to :func:`.name_for_scalar_relationship`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :param name_for_collection_relationship: callable function which will
							 | 
						||
| 
								 | 
							
								         be used to produce relationship names for collection-oriented
							 | 
						||
| 
								 | 
							
								         relationships.  Defaults to :func:`.name_for_collection_relationship`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :param generate_relationship: callable function which will be used to
							 | 
						||
| 
								 | 
							
								         actually generate :func:`_orm.relationship` and :func:`.backref`
							 | 
						||
| 
								 | 
							
								         constructs.  Defaults to :func:`.generate_relationship`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :param collection_class: the Python collection class that will be used
							 | 
						||
| 
								 | 
							
								         when a new :func:`_orm.relationship`
							 | 
						||
| 
								 | 
							
								         object is created that represents a
							 | 
						||
| 
								 | 
							
								         collection.  Defaults to ``list``.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :param schema: When present in conjunction with the
							 | 
						||
| 
								 | 
							
								         :paramref:`.AutomapBase.prepare.reflect` flag, is passed to
							 | 
						||
| 
								 | 
							
								         :meth:`_schema.MetaData.reflect`
							 | 
						||
| 
								 | 
							
								         to indicate the primary schema where tables
							 | 
						||
| 
								 | 
							
								         should be reflected from.  When omitted, the default schema in use
							 | 
						||
| 
								 | 
							
								         by the database connection is used.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								         .. versionadded:: 1.1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :param reflection_options: When present, this dictionary of options
							 | 
						||
| 
								 | 
							
								         will be passed to :meth:`_schema.MetaData.reflect`
							 | 
						||
| 
								 | 
							
								         to supply general reflection-specific options like ``only`` and/or
							 | 
						||
| 
								 | 
							
								         dialect-specific options like ``oracle_resolve_synonyms``.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								         .. versionadded:: 1.4
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        glbls = globals()
							 | 
						||
| 
								 | 
							
								        if classname_for_table is None:
							 | 
						||
| 
								 | 
							
								            classname_for_table = glbls["classname_for_table"]
							 | 
						||
| 
								 | 
							
								        if name_for_scalar_relationship is None:
							 | 
						||
| 
								 | 
							
								            name_for_scalar_relationship = glbls[
							 | 
						||
| 
								 | 
							
								                "name_for_scalar_relationship"
							 | 
						||
| 
								 | 
							
								            ]
							 | 
						||
| 
								 | 
							
								        if name_for_collection_relationship is None:
							 | 
						||
| 
								 | 
							
								            name_for_collection_relationship = glbls[
							 | 
						||
| 
								 | 
							
								                "name_for_collection_relationship"
							 | 
						||
| 
								 | 
							
								            ]
							 | 
						||
| 
								 | 
							
								        if generate_relationship is None:
							 | 
						||
| 
								 | 
							
								            generate_relationship = glbls["generate_relationship"]
							 | 
						||
| 
								 | 
							
								        if collection_class is None:
							 | 
						||
| 
								 | 
							
								            collection_class = list
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if autoload_with:
							 | 
						||
| 
								 | 
							
								            reflect = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if engine:
							 | 
						||
| 
								 | 
							
								            autoload_with = engine
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if reflect:
							 | 
						||
| 
								 | 
							
								            opts = dict(
							 | 
						||
| 
								 | 
							
								                schema=schema,
							 | 
						||
| 
								 | 
							
								                extend_existing=True,
							 | 
						||
| 
								 | 
							
								                autoload_replace=False,
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								            if reflection_options:
							 | 
						||
| 
								 | 
							
								                opts.update(reflection_options)
							 | 
						||
| 
								 | 
							
								            cls.metadata.reflect(autoload_with, **opts)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        with _CONFIGURE_MUTEX:
							 | 
						||
| 
								 | 
							
								            table_to_map_config = dict(
							 | 
						||
| 
								 | 
							
								                (m.local_table, m)
							 | 
						||
| 
								 | 
							
								                for m in _DeferredMapperConfig.classes_for_base(
							 | 
						||
| 
								 | 
							
								                    cls, sort=False
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            many_to_many = []
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            for table in cls.metadata.tables.values():
							 | 
						||
| 
								 | 
							
								                lcl_m2m, rem_m2m, m2m_const = _is_many_to_many(cls, table)
							 | 
						||
| 
								 | 
							
								                if lcl_m2m is not None:
							 | 
						||
| 
								 | 
							
								                    many_to_many.append((lcl_m2m, rem_m2m, m2m_const, table))
							 | 
						||
| 
								 | 
							
								                elif not table.primary_key:
							 | 
						||
| 
								 | 
							
								                    continue
							 | 
						||
| 
								 | 
							
								                elif table not in table_to_map_config:
							 | 
						||
| 
								 | 
							
								                    mapped_cls = type(
							 | 
						||
| 
								 | 
							
								                        classname_for_table(cls, table.name, table),
							 | 
						||
| 
								 | 
							
								                        (cls,),
							 | 
						||
| 
								 | 
							
								                        {"__table__": table},
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								                    map_config = _DeferredMapperConfig.config_for_cls(
							 | 
						||
| 
								 | 
							
								                        mapped_cls
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								                    cls.classes[map_config.cls.__name__] = mapped_cls
							 | 
						||
| 
								 | 
							
								                    table_to_map_config[table] = map_config
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            for map_config in table_to_map_config.values():
							 | 
						||
| 
								 | 
							
								                _relationships_for_fks(
							 | 
						||
| 
								 | 
							
								                    cls,
							 | 
						||
| 
								 | 
							
								                    map_config,
							 | 
						||
| 
								 | 
							
								                    table_to_map_config,
							 | 
						||
| 
								 | 
							
								                    collection_class,
							 | 
						||
| 
								 | 
							
								                    name_for_scalar_relationship,
							 | 
						||
| 
								 | 
							
								                    name_for_collection_relationship,
							 | 
						||
| 
								 | 
							
								                    generate_relationship,
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            for lcl_m2m, rem_m2m, m2m_const, table in many_to_many:
							 | 
						||
| 
								 | 
							
								                _m2m_relationship(
							 | 
						||
| 
								 | 
							
								                    cls,
							 | 
						||
| 
								 | 
							
								                    lcl_m2m,
							 | 
						||
| 
								 | 
							
								                    rem_m2m,
							 | 
						||
| 
								 | 
							
								                    m2m_const,
							 | 
						||
| 
								 | 
							
								                    table,
							 | 
						||
| 
								 | 
							
								                    table_to_map_config,
							 | 
						||
| 
								 | 
							
								                    collection_class,
							 | 
						||
| 
								 | 
							
								                    name_for_scalar_relationship,
							 | 
						||
| 
								 | 
							
								                    name_for_collection_relationship,
							 | 
						||
| 
								 | 
							
								                    generate_relationship,
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            for map_config in _DeferredMapperConfig.classes_for_base(cls):
							 | 
						||
| 
								 | 
							
								                map_config.map()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _sa_decl_prepare = True
							 | 
						||
| 
								 | 
							
								    """Indicate that the mapping of classes should be deferred.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The presence of this attribute name indicates to declarative
							 | 
						||
| 
								 | 
							
								    that the call to mapper() should not occur immediately; instead,
							 | 
						||
| 
								 | 
							
								    information about the table and attributes to be mapped are gathered
							 | 
						||
| 
								 | 
							
								    into an internal structure called _DeferredMapperConfig.  These
							 | 
						||
| 
								 | 
							
								    objects can be collected later using classes_for_base(), additional
							 | 
						||
| 
								 | 
							
								    mapping decisions can be made, and then the map() method will actually
							 | 
						||
| 
								 | 
							
								    apply the mapping.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The only real reason this deferral of the whole
							 | 
						||
| 
								 | 
							
								    thing is needed is to support primary key columns that aren't reflected
							 | 
						||
| 
								 | 
							
								    yet when the class is declared; everything else can theoretically be
							 | 
						||
| 
								 | 
							
								    added to the mapper later.  However, the _DeferredMapperConfig is a
							 | 
						||
| 
								 | 
							
								    nice interface in any case which exists at that not usually exposed point
							 | 
						||
| 
								 | 
							
								    at which declarative has the class and the Table but hasn't called
							 | 
						||
| 
								 | 
							
								    mapper() yet.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def _sa_raise_deferred_config(cls):
							 | 
						||
| 
								 | 
							
								        raise orm_exc.UnmappedClassError(
							 | 
						||
| 
								 | 
							
								            cls,
							 | 
						||
| 
								 | 
							
								            msg="Class %s is a subclass of AutomapBase.  "
							 | 
						||
| 
								 | 
							
								            "Mappings are not produced until the .prepare() "
							 | 
						||
| 
								 | 
							
								            "method is called on the class hierarchy."
							 | 
						||
| 
								 | 
							
								            % orm_exc._safe_cls_name(cls),
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def automap_base(declarative_base=None, **kw):
							 | 
						||
| 
								 | 
							
								    r"""Produce a declarative automap base.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This function produces a new base class that is a product of the
							 | 
						||
| 
								 | 
							
								    :class:`.AutomapBase` class as well a declarative base produced by
							 | 
						||
| 
								 | 
							
								    :func:`.declarative.declarative_base`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    All parameters other than ``declarative_base`` are keyword arguments
							 | 
						||
| 
								 | 
							
								    that are passed directly to the :func:`.declarative.declarative_base`
							 | 
						||
| 
								 | 
							
								    function.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param declarative_base: an existing class produced by
							 | 
						||
| 
								 | 
							
								     :func:`.declarative.declarative_base`.  When this is passed, the function
							 | 
						||
| 
								 | 
							
								     no longer invokes :func:`.declarative.declarative_base` itself, and all
							 | 
						||
| 
								 | 
							
								     other keyword arguments are ignored.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param \**kw: keyword arguments are passed along to
							 | 
						||
| 
								 | 
							
								     :func:`.declarative.declarative_base`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    if declarative_base is None:
							 | 
						||
| 
								 | 
							
								        Base = _declarative_base(**kw)
							 | 
						||
| 
								 | 
							
								    else:
							 | 
						||
| 
								 | 
							
								        Base = declarative_base
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return type(
							 | 
						||
| 
								 | 
							
								        Base.__name__,
							 | 
						||
| 
								 | 
							
								        (AutomapBase, Base),
							 | 
						||
| 
								 | 
							
								        {"__abstract__": True, "classes": util.Properties({})},
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def _is_many_to_many(automap_base, table):
							 | 
						||
| 
								 | 
							
								    fk_constraints = [
							 | 
						||
| 
								 | 
							
								        const
							 | 
						||
| 
								 | 
							
								        for const in table.constraints
							 | 
						||
| 
								 | 
							
								        if isinstance(const, ForeignKeyConstraint)
							 | 
						||
| 
								 | 
							
								    ]
							 | 
						||
| 
								 | 
							
								    if len(fk_constraints) != 2:
							 | 
						||
| 
								 | 
							
								        return None, None, None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    cols = sum(
							 | 
						||
| 
								 | 
							
								        [
							 | 
						||
| 
								 | 
							
								            [fk.parent for fk in fk_constraint.elements]
							 | 
						||
| 
								 | 
							
								            for fk_constraint in fk_constraints
							 | 
						||
| 
								 | 
							
								        ],
							 | 
						||
| 
								 | 
							
								        [],
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if set(cols) != set(table.c):
							 | 
						||
| 
								 | 
							
								        return None, None, None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return (
							 | 
						||
| 
								 | 
							
								        fk_constraints[0].elements[0].column.table,
							 | 
						||
| 
								 | 
							
								        fk_constraints[1].elements[0].column.table,
							 | 
						||
| 
								 | 
							
								        fk_constraints,
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def _relationships_for_fks(
							 | 
						||
| 
								 | 
							
								    automap_base,
							 | 
						||
| 
								 | 
							
								    map_config,
							 | 
						||
| 
								 | 
							
								    table_to_map_config,
							 | 
						||
| 
								 | 
							
								    collection_class,
							 | 
						||
| 
								 | 
							
								    name_for_scalar_relationship,
							 | 
						||
| 
								 | 
							
								    name_for_collection_relationship,
							 | 
						||
| 
								 | 
							
								    generate_relationship,
							 | 
						||
| 
								 | 
							
								):
							 | 
						||
| 
								 | 
							
								    local_table = map_config.local_table
							 | 
						||
| 
								 | 
							
								    local_cls = map_config.cls  # derived from a weakref, may be None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if local_table is None or local_cls is None:
							 | 
						||
| 
								 | 
							
								        return
							 | 
						||
| 
								 | 
							
								    for constraint in local_table.constraints:
							 | 
						||
| 
								 | 
							
								        if isinstance(constraint, ForeignKeyConstraint):
							 | 
						||
| 
								 | 
							
								            fks = constraint.elements
							 | 
						||
| 
								 | 
							
								            referred_table = fks[0].column.table
							 | 
						||
| 
								 | 
							
								            referred_cfg = table_to_map_config.get(referred_table, None)
							 | 
						||
| 
								 | 
							
								            if referred_cfg is None:
							 | 
						||
| 
								 | 
							
								                continue
							 | 
						||
| 
								 | 
							
								            referred_cls = referred_cfg.cls
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if local_cls is not referred_cls and issubclass(
							 | 
						||
| 
								 | 
							
								                local_cls, referred_cls
							 | 
						||
| 
								 | 
							
								            ):
							 | 
						||
| 
								 | 
							
								                continue
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            relationship_name = name_for_scalar_relationship(
							 | 
						||
| 
								 | 
							
								                automap_base, local_cls, referred_cls, constraint
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								            backref_name = name_for_collection_relationship(
							 | 
						||
| 
								 | 
							
								                automap_base, referred_cls, local_cls, constraint
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            o2m_kws = {}
							 | 
						||
| 
								 | 
							
								            nullable = False not in {fk.parent.nullable for fk in fks}
							 | 
						||
| 
								 | 
							
								            if not nullable:
							 | 
						||
| 
								 | 
							
								                o2m_kws["cascade"] = "all, delete-orphan"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if (
							 | 
						||
| 
								 | 
							
								                    constraint.ondelete
							 | 
						||
| 
								 | 
							
								                    and constraint.ondelete.lower() == "cascade"
							 | 
						||
| 
								 | 
							
								                ):
							 | 
						||
| 
								 | 
							
								                    o2m_kws["passive_deletes"] = True
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                if (
							 | 
						||
| 
								 | 
							
								                    constraint.ondelete
							 | 
						||
| 
								 | 
							
								                    and constraint.ondelete.lower() == "set null"
							 | 
						||
| 
								 | 
							
								                ):
							 | 
						||
| 
								 | 
							
								                    o2m_kws["passive_deletes"] = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            create_backref = backref_name not in referred_cfg.properties
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if relationship_name not in map_config.properties:
							 | 
						||
| 
								 | 
							
								                if create_backref:
							 | 
						||
| 
								 | 
							
								                    backref_obj = generate_relationship(
							 | 
						||
| 
								 | 
							
								                        automap_base,
							 | 
						||
| 
								 | 
							
								                        interfaces.ONETOMANY,
							 | 
						||
| 
								 | 
							
								                        backref,
							 | 
						||
| 
								 | 
							
								                        backref_name,
							 | 
						||
| 
								 | 
							
								                        referred_cls,
							 | 
						||
| 
								 | 
							
								                        local_cls,
							 | 
						||
| 
								 | 
							
								                        collection_class=collection_class,
							 | 
						||
| 
								 | 
							
								                        **o2m_kws
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    backref_obj = None
							 | 
						||
| 
								 | 
							
								                rel = generate_relationship(
							 | 
						||
| 
								 | 
							
								                    automap_base,
							 | 
						||
| 
								 | 
							
								                    interfaces.MANYTOONE,
							 | 
						||
| 
								 | 
							
								                    relationship,
							 | 
						||
| 
								 | 
							
								                    relationship_name,
							 | 
						||
| 
								 | 
							
								                    local_cls,
							 | 
						||
| 
								 | 
							
								                    referred_cls,
							 | 
						||
| 
								 | 
							
								                    foreign_keys=[fk.parent for fk in constraint.elements],
							 | 
						||
| 
								 | 
							
								                    backref=backref_obj,
							 | 
						||
| 
								 | 
							
								                    remote_side=[fk.column for fk in constraint.elements],
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								                if rel is not None:
							 | 
						||
| 
								 | 
							
								                    map_config.properties[relationship_name] = rel
							 | 
						||
| 
								 | 
							
								                    if not create_backref:
							 | 
						||
| 
								 | 
							
								                        referred_cfg.properties[
							 | 
						||
| 
								 | 
							
								                            backref_name
							 | 
						||
| 
								 | 
							
								                        ].back_populates = relationship_name
							 | 
						||
| 
								 | 
							
								            elif create_backref:
							 | 
						||
| 
								 | 
							
								                rel = generate_relationship(
							 | 
						||
| 
								 | 
							
								                    automap_base,
							 | 
						||
| 
								 | 
							
								                    interfaces.ONETOMANY,
							 | 
						||
| 
								 | 
							
								                    relationship,
							 | 
						||
| 
								 | 
							
								                    backref_name,
							 | 
						||
| 
								 | 
							
								                    referred_cls,
							 | 
						||
| 
								 | 
							
								                    local_cls,
							 | 
						||
| 
								 | 
							
								                    foreign_keys=[fk.parent for fk in constraint.elements],
							 | 
						||
| 
								 | 
							
								                    back_populates=relationship_name,
							 | 
						||
| 
								 | 
							
								                    collection_class=collection_class,
							 | 
						||
| 
								 | 
							
								                    **o2m_kws
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								                if rel is not None:
							 | 
						||
| 
								 | 
							
								                    referred_cfg.properties[backref_name] = rel
							 | 
						||
| 
								 | 
							
								                    map_config.properties[
							 | 
						||
| 
								 | 
							
								                        relationship_name
							 | 
						||
| 
								 | 
							
								                    ].back_populates = backref_name
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def _m2m_relationship(
							 | 
						||
| 
								 | 
							
								    automap_base,
							 | 
						||
| 
								 | 
							
								    lcl_m2m,
							 | 
						||
| 
								 | 
							
								    rem_m2m,
							 | 
						||
| 
								 | 
							
								    m2m_const,
							 | 
						||
| 
								 | 
							
								    table,
							 | 
						||
| 
								 | 
							
								    table_to_map_config,
							 | 
						||
| 
								 | 
							
								    collection_class,
							 | 
						||
| 
								 | 
							
								    name_for_scalar_relationship,
							 | 
						||
| 
								 | 
							
								    name_for_collection_relationship,
							 | 
						||
| 
								 | 
							
								    generate_relationship,
							 | 
						||
| 
								 | 
							
								):
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    map_config = table_to_map_config.get(lcl_m2m, None)
							 | 
						||
| 
								 | 
							
								    referred_cfg = table_to_map_config.get(rem_m2m, None)
							 | 
						||
| 
								 | 
							
								    if map_config is None or referred_cfg is None:
							 | 
						||
| 
								 | 
							
								        return
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    local_cls = map_config.cls
							 | 
						||
| 
								 | 
							
								    referred_cls = referred_cfg.cls
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    relationship_name = name_for_collection_relationship(
							 | 
						||
| 
								 | 
							
								        automap_base, local_cls, referred_cls, m2m_const[0]
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								    backref_name = name_for_collection_relationship(
							 | 
						||
| 
								 | 
							
								        automap_base, referred_cls, local_cls, m2m_const[1]
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    create_backref = backref_name not in referred_cfg.properties
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if table in table_to_map_config:
							 | 
						||
| 
								 | 
							
								        overlaps = "__*"
							 | 
						||
| 
								 | 
							
								    else:
							 | 
						||
| 
								 | 
							
								        overlaps = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if relationship_name not in map_config.properties:
							 | 
						||
| 
								 | 
							
								        if create_backref:
							 | 
						||
| 
								 | 
							
								            backref_obj = generate_relationship(
							 | 
						||
| 
								 | 
							
								                automap_base,
							 | 
						||
| 
								 | 
							
								                interfaces.MANYTOMANY,
							 | 
						||
| 
								 | 
							
								                backref,
							 | 
						||
| 
								 | 
							
								                backref_name,
							 | 
						||
| 
								 | 
							
								                referred_cls,
							 | 
						||
| 
								 | 
							
								                local_cls,
							 | 
						||
| 
								 | 
							
								                collection_class=collection_class,
							 | 
						||
| 
								 | 
							
								                overlaps=overlaps,
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            backref_obj = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        rel = generate_relationship(
							 | 
						||
| 
								 | 
							
								            automap_base,
							 | 
						||
| 
								 | 
							
								            interfaces.MANYTOMANY,
							 | 
						||
| 
								 | 
							
								            relationship,
							 | 
						||
| 
								 | 
							
								            relationship_name,
							 | 
						||
| 
								 | 
							
								            local_cls,
							 | 
						||
| 
								 | 
							
								            referred_cls,
							 | 
						||
| 
								 | 
							
								            overlaps=overlaps,
							 | 
						||
| 
								 | 
							
								            secondary=table,
							 | 
						||
| 
								 | 
							
								            primaryjoin=and_(
							 | 
						||
| 
								 | 
							
								                fk.column == fk.parent for fk in m2m_const[0].elements
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            secondaryjoin=and_(
							 | 
						||
| 
								 | 
							
								                fk.column == fk.parent for fk in m2m_const[1].elements
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            backref=backref_obj,
							 | 
						||
| 
								 | 
							
								            collection_class=collection_class,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        if rel is not None:
							 | 
						||
| 
								 | 
							
								            map_config.properties[relationship_name] = rel
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if not create_backref:
							 | 
						||
| 
								 | 
							
								                referred_cfg.properties[
							 | 
						||
| 
								 | 
							
								                    backref_name
							 | 
						||
| 
								 | 
							
								                ].back_populates = relationship_name
							 | 
						||
| 
								 | 
							
								    elif create_backref:
							 | 
						||
| 
								 | 
							
								        rel = generate_relationship(
							 | 
						||
| 
								 | 
							
								            automap_base,
							 | 
						||
| 
								 | 
							
								            interfaces.MANYTOMANY,
							 | 
						||
| 
								 | 
							
								            relationship,
							 | 
						||
| 
								 | 
							
								            backref_name,
							 | 
						||
| 
								 | 
							
								            referred_cls,
							 | 
						||
| 
								 | 
							
								            local_cls,
							 | 
						||
| 
								 | 
							
								            overlaps=overlaps,
							 | 
						||
| 
								 | 
							
								            secondary=table,
							 | 
						||
| 
								 | 
							
								            primaryjoin=and_(
							 | 
						||
| 
								 | 
							
								                fk.column == fk.parent for fk in m2m_const[1].elements
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            secondaryjoin=and_(
							 | 
						||
| 
								 | 
							
								                fk.column == fk.parent for fk in m2m_const[0].elements
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            back_populates=relationship_name,
							 | 
						||
| 
								 | 
							
								            collection_class=collection_class,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        if rel is not None:
							 | 
						||
| 
								 | 
							
								            referred_cfg.properties[backref_name] = rel
							 | 
						||
| 
								 | 
							
								            map_config.properties[
							 | 
						||
| 
								 | 
							
								                relationship_name
							 | 
						||
| 
								 | 
							
								            ].back_populates = backref_name
							 |