From 7e30b0ebd6d45e765415b9602f17594173690531 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sat, 28 Jan 2012 17:31:39 -0500 Subject: [PATCH] - [feature] New declarative reflection example added, illustrates how best to mix table reflection with declarative as well as uses some new features from [ticket:2356]. --- CHANGES | 5 ++ examples/declarative_reflection/__init__.py | 42 +++++++++++ .../declarative_reflection.py | 72 +++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 examples/declarative_reflection/__init__.py create mode 100644 examples/declarative_reflection/declarative_reflection.py diff --git a/CHANGES b/CHANGES index ed7a932064..df1cc83fdc 100644 --- a/CHANGES +++ b/CHANGES @@ -79,6 +79,11 @@ CHANGES relationship(). Thanks to Kent Bower for tests. [ticket:2374] + - [feature] New declarative reflection example + added, illustrates how best to mix table reflection + with declarative as well as uses some new features + from [ticket:2356]. + - sql - [feature] New reflection feature "autoload_replace"; when set to False on Table, the Table can be autoloaded diff --git a/examples/declarative_reflection/__init__.py b/examples/declarative_reflection/__init__.py new file mode 100644 index 0000000000..3c6be72889 --- /dev/null +++ b/examples/declarative_reflection/__init__.py @@ -0,0 +1,42 @@ +""" +Illustrates how to mix table reflection with Declarative, such that +the reflection process itself can take place **after** all classes +are defined. Declarative classes can also override column +definitions loaded from the database. + +At the core of this example is the ability to change how Declarative +assigns mappings to classes. The ``__mapper_cls__`` special attribute +is overridden to provide a function that gathers mapping requirements +as they are established, without actually creating the mapping. +Then, a second class-level method ``prepare()`` is used to iterate +through all mapping configurations collected, reflect the tables +named within and generate the actual mappers. + +The example is new in 0.7.5 and makes usage of the new +``autoload_replace`` flag on :class:`.Table` to allow declared +classes to override reflected columns. + +Usage example:: + + Base= declarative_base(cls=DeclarativeReflectedBase) + + class Foo(Base): + __tablename__ = 'foo' + bars = relationship("Bar") + + class Bar(Base): + __tablename__ = 'bar' + foo_id = Column(Integer, ForeignKey('foo.id')) + + Base.prepare(e) + + s = Session(e) + + s.add_all([ + Foo(bars=[Bar(data='b1'), Bar(data='b2')], data='f1'), + Foo(bars=[Bar(data='b3'), Bar(data='b4')], data='f2') + ]) + s.commit() + + +""" diff --git a/examples/declarative_reflection/declarative_reflection.py b/examples/declarative_reflection/declarative_reflection.py new file mode 100644 index 0000000000..88f191b3cf --- /dev/null +++ b/examples/declarative_reflection/declarative_reflection.py @@ -0,0 +1,72 @@ +from sqlalchemy import * +from sqlalchemy.orm import * +from sqlalchemy.ext.declarative import declarative_base, declared_attr + +class DeclarativeReflectedBase(object): + _mapper_args = [] + + @classmethod + def __mapper_cls__(cls, *args, **kw): + """Declarative will use this function in lieu of + calling mapper() directly. + + Collect each series of arguments and invoke + them when prepare() is called. + """ + + cls._mapper_args.append((args, kw)) + + @classmethod + def prepare(cls, engine): + """Reflect all the tables and map !""" + for args, kw in cls._mapper_args: + klass = args[0] + klass.__table__ = table = Table( + klass.__tablename__, + cls.metadata, + extend_existing=True, + autoload_replace=False, + autoload=True, + autoload_with=engine, + ) + klass.__mapper__ = mapper(klass, table, **kw) + + +if __name__ == '__main__': + Base= declarative_base(cls=DeclarativeReflectedBase) + + class Foo(Base): + __tablename__ = 'foo' + bars = relationship("Bar") + + class Bar(Base): + __tablename__ = 'bar' + foo_id = Column(Integer, ForeignKey('foo.id')) + + e = create_engine('sqlite://', echo=True) + e.execute(""" + create table foo( + id integer primary key, + data varchar(30) + ) + """) + + e.execute(""" + create table bar( + id integer primary key, + data varchar(30), + foo_id integer + ) + """) + + Base.prepare(e) + + s = Session(e) + + s.add_all([ + Foo(bars=[Bar(data='b1'), Bar(data='b2')], data='f1'), + Foo(bars=[Bar(data='b3'), Bar(data='b4')], data='f2') + ]) + s.commit() + for f in s.query(Foo): + print f.data, ",".join([b.data for b in f.bars]) \ No newline at end of file -- 2.47.2