diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-01-03 21:47:01 -0500 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-01-03 21:47:01 -0500 |
commit | cad46c3cdca2bb914bcfc233fcbd3647eaf8ffe9 (patch) | |
tree | 83d8ac7e1fb300f5aff536eca94f13ad959af06c /lib/sqlalchemy/ext/automap.py | |
parent | 31821011271bf2333b69954d53c3c922e39bf225 (diff) | |
download | sqlalchemy-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.py | 113 |
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", + ) + ) + ) + |