[Sqlalchemy-commits] [1301] sqlalchemy/branches/schema/doc/build: doc stuff
Brought to you by:
zzzeek
From: <co...@sq...> - 2006-04-19 23:07:37
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1301] sqlalchemy/branches/schema/doc/build: doc stuff</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1301</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-19 18:07:23 -0500 (Wed, 19 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>doc stuff</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemadocbuildcontentsqlconstructiontxt">sqlalchemy/branches/schema/doc/build/content/sqlconstruction.txt</a></li> <li><a href="#sqlalchemybranchesschemadocbuildcontenttutorialtxt">sqlalchemy/branches/schema/doc/build/content/tutorial.txt</a></li> <li><a href="#sqlalchemybranchesschemadocbuildtestdocspy">sqlalchemy/branches/schema/doc/build/testdocs.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyenginebasepy">sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyenginedefaultpy">sqlalchemy/branches/schema/lib/sqlalchemy/engine/default.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyschemapy">sqlalchemy/branches/schema/lib/sqlalchemy/schema.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemadocbuildcontentsqlconstructiontxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/sqlconstruction.txt (1300 => 1301)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/sqlconstruction.txt 2006-04-19 20:41:27 UTC (rev 1300) +++ sqlalchemy/branches/schema/doc/build/content/sqlconstruction.txt 2006-04-19 23:07:23 UTC (rev 1301) </span><span class="lines">@@ -1,4 +1,4 @@ </span><del>-Constructing SQL Queries via Python Expressions </del><ins>+Constructing SQL Queries via Python Expressions {@name=sqlconstruction} </ins><span class="cx"> =============================================== </span><span class="cx"> </span><span class="cx"> *Note:* This section describes how to use SQLAlchemy to construct SQL queries and receive result sets. It does *not* cover the object relational mapping capabilities of SQLAlchemy; that is covered later on in [datamapping](rel:datamapping). However, both areas of functionality work similarly in how selection criterion is constructed, so if you are interested just in ORM, you should probably skim through basic [sql_select_whereclause](rel:sql_select_whereclause) construction before moving on. </span></span></pre></div> <a id="sqlalchemybranchesschemadocbuildcontenttutorialtxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/tutorial.txt (1300 => 1301)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/tutorial.txt 2006-04-19 20:41:27 UTC (rev 1300) +++ sqlalchemy/branches/schema/doc/build/content/tutorial.txt 2006-04-19 23:07:23 UTC (rev 1301) </span><span class="lines">@@ -44,6 +44,7 @@ </span><span class="cx"> </span><span class="cx"> SQLAlchemy provides the entire namespace of everything you'll need under the module name `sqlalchemy`. For the purposes of this tutorial, we will import its full list of symbols into our own local namespace. We also will be using a *mod* that provides access to thread-managed connection and session objects, which will greatly simplifies our code. A *mod* is a module that augments the core functionality of SQLAlchemy with additional functionality, and only needs to be imported once within an application. </span><span class="cx"> </span><ins>+ {python} </ins><span class="cx"> >>> from sqlalchemy import * </span><span class="cx"> >>> import sqlalchemy.mods.threadlocal </span><span class="cx"> </span><span class="lines">@@ -51,25 +52,48 @@ </span><span class="cx"> </span><span class="cx"> After our imports, the next thing we need is a handle to the desired database, represented by an `Engine` object. This object handles the business of managing connections and dealing with the specifics of a particular database. Below, we will make a SQLite connection to a file-based database called "tutorial.db". </span><span class="cx"> </span><ins>+ {python} </ins><span class="cx"> >>> db = create_engine('sqlite:///tutorial.db') </span><span class="cx"> </span><span class="cx"> </span><span class="cx"> For full information on creating database engines, including those for SQLite and others, see [dbengine](rel:dbengine). </span><span class="cx"> </span><del>-### Creating a Table {@name=table} </del><ins>+Working with Database Objects {@name=schemasql} +----------------------------------------------- </ins><span class="cx"> </span><ins>+### Defining Metadata, Binding to Engines {@name=metadata} + </ins><span class="cx"> A core philosophy of SQLAlchemy is that tables and domain classes are different beasts. For this reason, SQLAlchemy provides constructs that represent tables by themselves (known as *table metadata*). So we will begin by constructing table metadata objects and performing SQL operations with them directly, keeping in mind that there is also an Object Relational Mapper (ORM) which does the same thing except via domain models. </span><span class="cx"> </span><span class="cx"> Firstly, your Tables have to belong to a collection called `MetaData`. We will create a handy form of `MetaData` that automatically connects to our `Engine` (connecting a schema object to an Engine is called *binding*): </span><span class="cx"> </span><ins>+ {python} </ins><span class="cx"> >>> metadata = BoundMetaData(db) </span><span class="cx"> </span><span class="cx"> An equivalent operation is to create the `BoundMetaData` object directly with an Engine URL, which calls the `create_engine` call for us: </span><span class="cx"> </span><ins>+ {python} </ins><span class="cx"> >>> metadata = BoundMetaData('sqlite:///tutorial.db') </span><span class="cx"> </span><ins>+When creating MetaData objects that are bound to Engines, all objects within the MetaData or which are derived from it are similarly bound to that engine; this includes tables and SQL statement constructs. A SQL construct that is bound to an Engine supports "connectionless execution", that is, each object knows how to retrieve connections and use them automatically for its own execution, without you having to worry about it. However, note that the "binding" of schema and SQL constructs to engines is **entirely optional**. SQLAlchemy includes full support for explicit Connections used with schema and SQL constructs that are entirely unbound to any Engine. + +For the purposes of this tutorial, we will stick with "bound" objects. There is also a more flexible "dynamic" metadata object that supports runtime binding to multiple engines: + + {python} + >>> dynamic = DynamicMetaData() # create a Dynamic metadata object + >>> dynamic.connect('sqlite:///:memory:') # connect it to SQLite + >>> dynamic.connect('postgres:///scott:tiger@localhost/mydb') # connect it to PostGres + + >>> myengine = create_engine('mysql:///127.0.0.1') + >>> dynamic.connect(myengine) # connect to an externally-defined engine + +The `DynamicMetaData` object binds to different engines on a thread local basis. This means that one thread of your application can be connected to one database, while another is connected to a different database. The `DynamicMetaData` object also keeps a reference to each bound engine internally, so that each connection string is only initialized once. + +### Creating a Table {@name=table} + </ins><span class="cx"> With `metadata` as our established home for tables, lets make a Table for it: </span><span class="cx"> </span><ins>+ {python} </ins><span class="cx"> >>> users = Table('users', metadata, </span><span class="cx"> ... Column('user_id', Integer, primary_key=True), </span><span class="cx"> ... Column('user_name', String(40)), </span><span class="lines">@@ -78,6 +102,7 @@ </span><span class="cx"> </span><span class="cx"> As you might have guessed, we have just defined a table named `users` which has three columns: `user_id` (which is a primary key column), `user_name` and `password`. Currently it is just an object that may not correspond to an existing table in your database. To actually create the table, we use the `create()` method. To make it interesting, we will have SQLAlchemy to echo the SQL statements it sends to the database, by setting the `echo` flag on the `Engine` associated with our `BoundMetaData`: </span><span class="cx"> </span><ins>+ {python} </ins><span class="cx"> >>> metadata.engine.echo = True </span><span class="cx"> >>> users.create() # doctest:+ELLIPSIS,+NORMALIZE_WHITESPACE </span><span class="cx"> CREATE TABLE users( </span><span class="lines">@@ -86,10 +111,10 @@ </span><span class="cx"> password VARCHAR(80) </span><span class="cx"> ) </span><span class="cx"> ... </span><del>- >>> metadata.engine.echo = False # you can skip this if you want to keep logging SQL statements </del><span class="cx"> </span><span class="cx"> Alternatively, the `users` table might already exist (such as, if you're running examples from this tutorial for the second time), in which case you can just skip the `create()` method call. You can even skip defining the individual columns in the `users` table and ask SQLAlchemy to load its definition from the database: </span><span class="cx"> </span><ins>+ {python} </ins><span class="cx"> >>> users = Table('users', metadata, autoload=True) </span><span class="cx"> >>> list(users.columns)[0].name </span><span class="cx"> 'user_id' </span><span class="lines">@@ -98,41 +123,58 @@ </span><span class="cx"> </span><span class="cx"> ### Inserting Rows </span><span class="cx"> </span><del>-Inserting is achieved via the `insert()` method, which defines a *clause object* representing an INSERT statement: </del><ins>+Inserting is achieved via the `insert()` method, which defines a *clause object* (known as a `ClauseElement`) representing an INSERT statement: </ins><span class="cx"> </span><ins>+ {python} </ins><span class="cx"> >>> i = users.insert() </span><span class="cx"> >>> i # doctest:+ELLIPSIS </span><span class="cx"> <sqlalchemy.sql.Insert object at 0x...> </span><span class="cx"> >>> print i </span><span class="cx"> INSERT INTO users (user_id, user_name, password) VALUES (?, ?, ?) </span><span class="cx"> </span><del>-The `execute()` method of the clause object executes the statement at the database level: </del><ins>+Since we created this insert statement object from the `users` table which is bound to our `Engine`, the statement itself is also bound to the `Engine`, and supports executing itself. The `execute()` method of the clause object will *compile* the object into a string according to the underlying *dialect* of the Engine to which the statement is bound, and then executes the resulting statement. </ins><span class="cx"> </span><del>- >>> for name in ['Tom', 'Dick', 'Harry']: # doctest:+ELLIPSIS </del><ins>+ {python} + >>> for name in ['Tom', 'Dick', 'Harry']: # doctest:+ELLIPSIS,+NORMALIZE_WHITESPACE </ins><span class="cx"> ... i.execute(user_name = name) </span><ins>+ INSERT INTO users (user_name) VALUES (?) + ['Tom'] </ins><span class="cx"> <sqlalchemy.engine.base.ResultProxy instance at 0x...> </span><del>- ... </del><ins>+ INSERT INTO users (user_name) VALUES (?) + ['Dick'] + <sqlalchemy.engine.base.ResultProxy instance at 0x...> + INSERT INTO users (user_name) VALUES (?) + ['Harry'] + <sqlalchemy.engine.base.ResultProxy instance at 0x...> + </ins><span class="cx"> >>> i.execute(user_name = 'Mary', password = 'secure') # doctest:+ELLIPSIS </span><ins>+ INSERT INTO users (user_name, password) VALUES (?, ?) + ['Mary', 'secure'] </ins><span class="cx"> <sqlalchemy.engine.base.ResultProxy instance at 0x...> </span><span class="cx"> </span><del>-When constructing clause objects, SQLAlchemy will bind all literal values into bind parameters, according to the paramstyle of the underlying DBAPI. This allows for better performance, as the database may cache a compiled representation of the statement and reuse it for new executions, substituting the new values. Also, when using bound values, you need not worry about [SQL injection][] attacks. </del><ins>+Note that the `VALUES` clause of each `INSERT` statement was automatically adjusted to correspond to the parameters sent to the `execute()` method. This is because the compilation step of a `ClauseElement` takes into account not just the constructed SQL object and the specifics of the type of database being used, but the execution parameters sent along as well. </ins><span class="cx"> </span><del>-[SQL injection]: http://en.wikipedia.org/wiki/SQL_injection </del><ins>+When constructing clause objects, SQLAlchemy will bind all literal values into bind parameters. On the construction side, bind parameters are always treated as named parameters. At compilation time, SQLAlchemy will convert them their proper format, based on the paramstyle of the underlying DBAPI. This works equally well for all named and positional bind parameter formats described in the DBAPI specification. </ins><span class="cx"> </span><del>-Documentation on inserting: [sql_insert](rel:sql_insert). </del><ins>+Documentation on inserting: [sqlconstruction_insert](rel:sqlconstruction_insert). </ins><span class="cx"> </span><del>-### Constructing Queries </del><ins>+### Selecting </ins><span class="cx"> </span><span class="cx"> Let's check that the data we have put into `users` table is actually there. The procedure is analogous to the insert example above, except you now call the `select()` method off the `users` table: </span><span class="cx"> </span><ins>+ {python} </ins><span class="cx"> >>> s = users.select() </span><span class="cx"> >>> print s </span><span class="cx"> SELECT users.user_id, users.user_name, users.password </span><span class="cx"> FROM users </span><span class="cx"> >>> r = s.execute() </span><del>- </del><ins>+ SELECT users.user_id, users.user_name, users.password + FROM users + [] + </ins><span class="cx"> This time, we won't ignore the return value of `execute()`: </span><span class="cx"> </span><ins>+ {python} </ins><span class="cx"> >>> r # doctest:+ELLIPSIS </span><span class="cx"> <sqlalchemy.engine.base.ResultProxy instance at 0x...> </span><span class="cx"> >>> r.keys </span><span class="lines">@@ -143,16 +185,44 @@ </span><span class="cx"> >>> r.fetchall() </span><span class="cx"> [(2, u'Dick', None), (3, u'Harry', None), (4, u'Mary', u'secure')] </span><span class="cx"> </span><del>-Documentation on selecting: [sql_select](rel:sql_select). </del><ins>+Query criterion for the select can also be specified as regular Python expressions, using the column objects in the Table as a base: </ins><span class="cx"> </span><del>-### Related Table </del><ins>+ {python} + >>> r = users.select(users.c.user_name=='Harry').execute() + SELECT users.user_id, users.user_name, users.password + FROM users + WHERE users.user_name = ? + ['Harry'] + >>> r.fetchall() + [(3, u'Harry', None)] + +Pretty much the full range of standard SQL operations are supported as constructed Python expressions, including joins, ordering, grouping, functions, correlated subqueries, unions, etc. Documentation on selecting: [sqlconstruction_select](rel:sqlconstruction_select). </ins><span class="cx"> </span><del>-Main documentation: [sql](rel:sql). </del><ins>+### Table Relationships </ins><span class="cx"> </span><del>-### Fancier Querying {@name=fancyquery} </del><ins>+Lets create a second table, `email_addresses`, which references the `users` table. To define the relationship between the two tables, we will use the `ForeignKey` construct. We will also issue the `CREATE` statement for the table in one step: </ins><span class="cx"> </span><del>-Main documentation: [sql](rel:sql). </del><ins>+ {python} + >>> email_addresses = Table('email_addresses', metadata, + ... Column('address_id', Integer, primary_key=True), + ... Column('email_address', String(100), nullable=False), + ... Column('user_id', Integer, ForeignKey('users.user_id'))).create() # doctest:+ELLIPSIS,+NORMALIZE_WHITESPACE + CREATE TABLE email_addresses( + address_id INTEGER NOT NULL PRIMARY KEY, + email_address VARCHAR(100) NOT NULL, + user_id INTEGER REFERENCES users(user_id) + ) + ... </ins><span class="cx"> </span><ins>+With two related tables, we can now construct a join amongst them, like this: + + {python} + >>> r = users.join(email_addresses).select().execute() + SELECT users.user_id, users.user_name, users.password, email_addresses.address_id, email_addresses.email_address, email_addresses.user_id + FROM users JOIN email_addresses ON users.user_id = email_addresses.user_id + [] + + </ins><span class="cx"> ### Data Mapping {@name=mapping} </span><span class="cx"> </span><span class="cx"> Main documentation: [datamapping](rel:datamapping), [adv_datamapping](rel:adv_datamapping). </span></span></pre></div> <a id="sqlalchemybranchesschemadocbuildtestdocspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/testdocs.py (1300 => 1301)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/testdocs.py 2006-04-19 20:41:27 UTC (rev 1300) +++ sqlalchemy/branches/schema/doc/build/testdocs.py 2006-04-19 23:07:23 UTC (rev 1301) </span><span class="lines">@@ -46,16 +46,16 @@ </span><span class="cx"> </span><span class="cx"> return runner.failures, runner.tries </span><span class="cx"> </span><del>-def replace_file(s, oldfile, newfile): - engine = r"sqlite:///" + oldfile </del><ins>+def replace_file(s, newfile): + engine = r"'(sqlite|postgres|mysql):///.*'" </ins><span class="cx"> engine = re.compile(engine, re.MULTILINE) </span><del>- s, n = re.subn(engine, "sqlite:///" + newfile, s) </del><ins>+ s, n = re.subn(engine, "'sqlite:///" + newfile + "'", s) </ins><span class="cx"> if not n: </span><span class="cx"> raise ValueError("Couldn't find suitable create_engine call to replace '%s' in it" % oldfile) </span><span class="cx"> return s </span><span class="cx"> </span><span class="cx"> filename = 'content/tutorial.txt' </span><span class="cx"> s = open(filename).read() </span><del>-s = replace_file(s, 'tutorial.db', ':memory:') </del><ins>+s = replace_file(s, ':memory:') </ins><span class="cx"> teststring(s, filename) </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyenginebasepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py (1300 => 1301)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py 2006-04-19 20:41:27 UTC (rev 1300) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py 2006-04-19 23:07:23 UTC (rev 1301) </span><span class="lines">@@ -26,6 +26,11 @@ </span><span class="cx"> </span><span class="cx"> paramstyle - the paramstyle to be used (some DBAPIs support multiple paramstyles) </span><span class="cx"> </span><ins>+ supports_autoclose_results - usually True; if False, indicates that rows returned by fetchone() + might not be just plain tuples, and may be "live" proxy objects which still require the cursor + to be open in order to be read (such as pyPgSQL which has active filehandles for BLOBs). in that + case, an auto-closing ResultProxy cannot automatically close itself after results are consumed. + </ins><span class="cx"> convert_unicode - True if unicode conversion should be applied to all str types </span><span class="cx"> </span><span class="cx"> encoding - type of encoding to use for unicode, usually defaults to 'utf-8' </span><span class="lines">@@ -451,7 +456,7 @@ </span><span class="cx"> def close(self): </span><span class="cx"> if not self.closed: </span><span class="cx"> self.closed = True </span><del>- if self.connection.close_with_result: </del><ins>+ if self.connection.close_with_result and self.dialect.supports_autoclose_results: </ins><span class="cx"> self.connection.close() </span><span class="cx"> def _get_col(self, row, key): </span><span class="cx"> if isinstance(key, schema.Column) or isinstance(key, sql.ColumnElement): </span><span class="lines">@@ -504,6 +509,9 @@ </span><span class="cx"> if self.echo: self.engine.log(repr(row)) </span><span class="cx"> return RowProxy(self, row) </span><span class="cx"> else: </span><ins>+ # controversy! can we auto-close the cursor after results are consumed ? + # what if the returned rows are still hanging around, and are "live" objects + # and not just plain tuples ? </ins><span class="cx"> self.close() </span><span class="cx"> return None </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyenginedefaultpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/engine/default.py (1300 => 1301)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/default.py 2006-04-19 20:41:27 UTC (rev 1300) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/default.py 2006-04-19 23:07:23 UTC (rev 1301) </span><span class="lines">@@ -40,6 +40,7 @@ </span><span class="cx"> def __init__(self, default_ordering=False, convert_unicode=False, encoding='utf-8', **kwargs): </span><span class="cx"> self.default_ordering=default_ordering </span><span class="cx"> self.convert_unicode = convert_unicode </span><ins>+ self.supports_autoclose_results = True </ins><span class="cx"> self.encoding = encoding </span><span class="cx"> self.positional = False </span><span class="cx"> self.paramstyle = 'named' </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyschemapy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/schema.py (1300 => 1301)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/schema.py 2006-04-19 20:41:27 UTC (rev 1300) +++ sqlalchemy/branches/schema/lib/sqlalchemy/schema.py 2006-04-19 23:07:23 UTC (rev 1301) </span><span class="lines">@@ -183,7 +183,6 @@ </span><span class="cx"> def accept_schema_visitor(self, visitor): </span><span class="cx"> """traverses the given visitor across the Column objects inside this Table, </span><span class="cx"> then calls the visit_table method on the visitor.""" </span><del>- print "TABLE ACCEPT VISITOR, C IS", [c for c in self.columns] </del><span class="cx"> for c in self.columns: </span><span class="cx"> c.accept_schema_visitor(visitor) </span><span class="cx"> return visitor.visit_table(self) </span><span class="lines">@@ -627,9 +626,15 @@ </span><span class="cx"> self.__engines[engine_or_url] = e </span><span class="cx"> self.context._engine = e </span><span class="cx"> else: </span><ins>+ if not self.__engines.has_key(engine_or_url): + self.__engines[engine_or_url] = engine_or_url </ins><span class="cx"> self.context._engine = engine_or_url </span><span class="cx"> def is_bound(self): </span><span class="cx"> return s.context._engine is not None </span><ins>+ def dispose(self): + """disposes all Engines to which this DynamicMetaData has been connected.""" + for e in self.__engines.values(): + e.dispose() </ins><span class="cx"> engine=property(lambda s:s.context._engine) </span><span class="cx"> </span><span class="cx"> class SchemaVisitor(sql.ClauseVisitor): </span></span></pre> </div> </div> </body> </html> |