summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/ext/automap.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2014-01-03 21:47:01 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2014-01-03 21:47:01 -0500
commitcad46c3cdca2bb914bcfc233fcbd3647eaf8ffe9 (patch)
tree83d8ac7e1fb300f5aff536eca94f13ad959af06c /lib/sqlalchemy/ext/automap.py
parent31821011271bf2333b69954d53c3c922e39bf225 (diff)
downloadsqlalchemy-cad46c3cdca2bb914bcfc233fcbd3647eaf8ffe9.tar.gz
- hypothetical "automap" feature, would extend DeferredReflection to create classes
against the remaining tables within the given metadata.
Diffstat (limited to 'lib/sqlalchemy/ext/automap.py')
-rw-r--r--lib/sqlalchemy/ext/automap.py113
1 files changed, 113 insertions, 0 deletions
diff --git a/lib/sqlalchemy/ext/automap.py b/lib/sqlalchemy/ext/automap.py
new file mode 100644
index 000000000..9aa9be8a4
--- /dev/null
+++ b/lib/sqlalchemy/ext/automap.py
@@ -0,0 +1,113 @@
+# ext/automap.py
+# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
+#
+# This module is part of SQLAlchemy and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""Define an extension to the :mod:`sqlalchemy.ext.declarative` system
+which automatically generates mapped classes and attributes from a database
+schema, typically one which is reflected.
+
+Features:
+
+* The given :class:`.MetaData` structure may or may not be reflected.
+ :mod:`.automap` isn't dependent on this.
+
+* Classes which are known to be present in the :mod:`.automap` structure
+ can be pre-declared with known attributes and settings.
+
+* The system integrates with the featureset of :mod:`.declarative`, including
+ support of mixins, abstract bases, interoperability with non-automapped
+ classes.
+
+* The system can build out classes for an entire :class:`.MetaData` structure
+ or for individual :class:`.Table` objects.
+
+* Relationships between classes are generated based on foreign keys, including
+ that simple many-to-many relationships are also detectable.
+
+* Hooks are provided for many key points, including:
+
+ * A function which converts the name of table into a mapped class
+
+ * A function which receives a :class:`.Column` object to be mapped and
+ produces the element to be part of the mapping.
+
+ * A function which receives two classes which should generate a
+ :func:`.relationship` and produces the actual :func:`.relationship`.
+
+ * Functions which produce attribute names; given a scalar column,
+ or a class name for a scalar or collection reference, produce an attribute
+ name.
+
+"""
+from sqlalchemy.ext.declarative import declarative_base, DeferredReflection
+from sqlalchemy.ext.declarative.base import _MapperConfig
+from sqlalchemy.schema import ForeignKeyConstraint
+from sqlalchemy.orm import relationship, backref
+from sqlalchemy.util import Properties
+
+def _classname_for_table(table):
+ return table.name
+
+def automap_base(metadata, **kw):
+ Base = declarative_base()
+
+ class BaseThing(DeferredReflection, Base):
+ registry = Properties({})
+
+ @classmethod
+ def prepare(cls, engine):
+ cls.metadata.reflect(engine)
+
+ table_to_map_config = dict(
+ (m.local_table, m)
+ for m in _MapperConfig.configs.values()
+ if issubclass(m.cls, cls)
+ )
+
+ for table in cls.metadata.tables.values():
+ if table not in table_to_map_config:
+ mapped_cls = type(
+ _classname_for_table(table),
+ (BaseThing, ),
+ {}
+ )
+ map_config = _MapperConfig.configs[mapped_cls]
+ table_to_map_config[table] = map_config
+
+ for map_config in table_to_map_config.values():
+ _relationships_for_fks(map_config, table_to_map_config)
+ super(BaseThing, cls).prepare(engine)
+
+ @classmethod
+ def _reflect_table(cls, table, engine):
+ pass
+
+ @classmethod
+ def _sa_decl_prepare(cls, local_table, engine):
+ if engine is not None:
+ super(BaseThing, cls)._sa_decl_prepare(local_table, engine)
+ # expected that the Table is present.
+ _relationships_for_fks(cls, local_table)
+
+def _relationships_for_fks(map_config, table_to_map_config):
+ local_table = map_config.local_table
+ local_cls = map_config.cls
+ for constraint in local_table.constraints:
+ if isinstance(constraint, ForeignKeyConstraint):
+ fks = constraint.elements
+ referred_table = fks[0].column.table
+ referred_cls = table_to_map_config[referred_table].local_cls
+
+ setattr(
+ local_cls,
+ referred_cls.__name__.lower(),
+ relationship(referred_cls,
+ foreign_keys=[fk.parent for fk in constraint.elements],
+ backref=backref(
+ local_cls.__name__.lower() + "_collection",
+ )
+ )
+ )
+