[Sqlalchemy-commits] [1327] sqlalchemy/branches/schema/test: added create_all/drop_all to metadata
Brought to you by:
zzzeek
From: <co...@sq...> - 2006-04-23 23:18:15
|
<!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>[1327] sqlalchemy/branches/schema/test: added create_all/drop_all to metadata</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1327</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-23 18:17:53 -0500 (Sun, 23 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>added create_all/drop_all to metadata</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemydatabasespostgrespy">sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyenginebasepy">sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymodsthreadlocalpy">sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyorm__init__py">sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormutilpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/util.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyschemapy">sqlalchemy/branches/schema/lib/sqlalchemy/schema.py</a></li> <li><a href="#sqlalchemybranchesschematestreflectionpy">sqlalchemy/branches/schema/test/reflection.py</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemysql_utilpy">sqlalchemy/branches/schema/lib/sqlalchemy/sql_util.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemydatabasespostgrespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py (1326 => 1327)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py 2006-04-23 22:19:06 UTC (rev 1326) +++ sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py 2006-04-23 23:17:53 UTC (rev 1327) </span><span class="lines">@@ -273,6 +273,13 @@ </span><span class="cx"> def dbapi(self): </span><span class="cx"> return self.module </span><span class="cx"> </span><ins>+ def has_table(self, connection, table_name): + """ + return boolean whether or not the engine/schema contains this table + """ + cursor = connection.execute("""select relname from pg_class where relname = %(name)s""", {'name':table_name}) + return bool( not not cursor.rowcount ) + </ins><span class="cx"> def reflecttable(self, connection, table): </span><span class="cx"> if self.version == 2: </span><span class="cx"> ischema_names = pg2_ischema_names </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyenginebasepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py (1326 => 1327)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py 2006-04-23 22:19:06 UTC (rev 1326) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py 2006-04-23 23:17:53 UTC (rev 1327) </span><span class="lines">@@ -83,6 +83,8 @@ </span><span class="cx"> def reflecttable(self, connection, table): </span><span class="cx"> """given an Connection and a Table object, reflects its columns and properties from the database.""" </span><span class="cx"> raise NotImplementedError() </span><ins>+ def has_table(self, connection, table_name): + raise NotImplementedError() </ins><span class="cx"> def dbapi(self): </span><span class="cx"> """subclasses override this method to provide the DBAPI module used to establish </span><span class="cx"> connections.""" </span><span class="lines">@@ -245,7 +247,8 @@ </span><span class="cx"> return self.engine.reflecttable(table, connection=self, **kwargs) </span><span class="cx"> def default_schema_name(self): </span><span class="cx"> return self.engine.dialect.get_default_schema_name(self) </span><del>- </del><ins>+ def run_callable(self, callable_): + callable_(self) </ins><span class="cx"> def _execute_raw(self, statement, parameters=None, cursor=None, echo=None, context=None, **kwargs): </span><span class="cx"> if cursor is None: </span><span class="cx"> cursor = self.connection.cursor() </span><span class="lines">@@ -368,7 +371,18 @@ </span><span class="cx"> finally: </span><span class="cx"> if connection is None: </span><span class="cx"> conn.close() </span><del>- </del><ins>+ + def run_callable(self, callable_, connection=None): + if connection is None: + conn = self.contextual_connect() + else: + conn = connection + try: + return callable_(conn) + finally: + if connection is None: + conn.close() + </ins><span class="cx"> def execute(self, *args, **kwargs): </span><span class="cx"> connection = self.contextual_connect() </span><span class="cx"> return connection.execute(*args, **kwargs) </span><span class="lines">@@ -400,7 +414,9 @@ </span><span class="cx"> finally: </span><span class="cx"> if connection is None: </span><span class="cx"> conn.close() </span><del>- </del><ins>+ def has_table(self, table_name): + return self.run_callable(lambda c: self.dialect.has_table(c, table_name)) + </ins><span class="cx"> def raw_connection(self): </span><span class="cx"> """returns a DBAPI connection.""" </span><span class="cx"> return self.connection_provider.get_connection() </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymodsthreadlocalpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py (1326 => 1327)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py 2006-04-23 22:19:06 UTC (rev 1326) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py 2006-04-23 23:17:53 UTC (rev 1327) </span><span class="lines">@@ -28,7 +28,7 @@ </span><span class="cx"> return get_session().commit(obj) </span><span class="cx"> def get_session(self, obj=None): </span><span class="cx"> return get_session(obj=obj) </span><del>- def flush(self, obj): </del><ins>+ def flush(self, obj=None): </ins><span class="cx"> """flushes the current UnitOfWork transaction. if a transaction was begun </span><span class="cx"> via begin(), flushes only those objects that were created, modified, or deleted </span><span class="cx"> since that begin statement. otherwise flushes all objects that have been </span><span class="lines">@@ -91,7 +91,7 @@ </span><span class="cx"> m = mapper(class_, *args, **params) </span><span class="cx"> class_.mapper = m </span><span class="cx"> class_.get = m.get </span><del>- class_.select = m.selectobj </del><ins>+ class_.select = m.select </ins><span class="cx"> class_.select_by = m.select_by </span><span class="cx"> class_.selectone = m.selectone </span><span class="cx"> class_.get_by = m.get_by </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyorm__init__py"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py (1326 => 1327)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py 2006-04-23 22:19:06 UTC (rev 1326) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py 2006-04-23 23:17:53 UTC (rev 1327) </span><span class="lines">@@ -12,12 +12,9 @@ </span><span class="cx"> import sqlalchemy.schema as schema </span><span class="cx"> import sqlalchemy.engine as engine </span><span class="cx"> import sqlalchemy.util as util </span><del>-import session </del><span class="cx"> from exceptions import * </span><del>-import types as types </del><span class="cx"> from mapper import * </span><span class="cx"> from properties import * </span><del>-import mapper as mapperlib </del><span class="cx"> </span><span class="cx"> __all__ = ['relation', 'backref', 'eagerload', 'lazyload', 'noload', 'deferred', 'defer', 'undefer', </span><span class="cx"> 'mapper', 'clear_mappers', 'sql', 'extension', 'class_mapper', 'object_mapper', 'MapperExtension', </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py (1326 => 1327)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-04-23 22:19:06 UTC (rev 1326) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-04-23 23:17:53 UTC (rev 1327) </span><span class="lines">@@ -9,6 +9,7 @@ </span><span class="cx"> import sqlalchemy.schema as schema </span><span class="cx"> import sqlalchemy.util as util </span><span class="cx"> import util as mapperutil </span><ins>+import sqlalchemy.sql_util as sqlutil </ins><span class="cx"> import sync </span><span class="cx"> from sqlalchemy.exceptions import * </span><span class="cx"> import query </span><span class="lines">@@ -107,7 +108,7 @@ </span><span class="cx"> # stricter set of tables to create "sync rules" by,based on the immediate </span><span class="cx"> # inherited table, rather than all inherited tables </span><span class="cx"> self._synchronizer = sync.ClauseSynchronizer(self, self, sync.ONETOMANY) </span><del>- self._synchronizer.compile(self.table.onclause, util.HashSet([inherits.noninherited_table]), mapperutil.TableFinder(table)) </del><ins>+ self._synchronizer.compile(self.table.onclause, util.HashSet([inherits.noninherited_table]), sqlutil.TableFinder(table)) </ins><span class="cx"> # the old rule </span><span class="cx"> #self._synchronizer.compile(self.table.onclause, inherits.tables, TableFinder(table)) </span><span class="cx"> else: </span><span class="lines">@@ -122,7 +123,7 @@ </span><span class="cx"> </span><span class="cx"> # locate all tables contained within the "table" passed in, which </span><span class="cx"> # may be a join or other construct </span><del>- self.tables = mapperutil.TableFinder(self.table) </del><ins>+ self.tables = sqlutil.TableFinder(self.table) </ins><span class="cx"> </span><span class="cx"> # determine primary key columns, either passed in, or get them from our set of tables </span><span class="cx"> self.pks_by_table = {} </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormutilpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/util.py (1326 => 1327)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/util.py 2006-04-23 22:19:06 UTC (rev 1326) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/util.py 2006-04-23 23:17:53 UTC (rev 1327) </span><span class="lines">@@ -4,34 +4,8 @@ </span><span class="cx"> # This module is part of SQLAlchemy and is released under </span><span class="cx"> # the MIT License: http://www.opensource.org/licenses/mit-license.php </span><span class="cx"> </span><del>- -import sqlalchemy.sql as sql </del><span class="cx"> import sets </span><span class="cx"> </span><del>-class TableFinder(sql.ClauseVisitor): - """given a Clause, locates all the Tables within it into a list.""" - def __init__(self, table, check_columns=False): - self.tables = [] - self.check_columns = check_columns - if table is not None: - table.accept_visitor(self) - def visit_table(self, table): - self.tables.append(table) - def __len__(self): - return len(self.tables) - def __getitem__(self, i): - return self.tables[i] - def __iter__(self): - return iter(self.tables) - def __contains__(self, obj): - return obj in self.tables - def __add__(self, obj): - return self.tables + list(obj) - def visit_column(self, column): - if self.check_columns: - column.table.accept_visitor(self) - - </del><span class="cx"> class CascadeOptions(object): </span><span class="cx"> """keeps track of the options sent to relation().cascade""" </span><span class="cx"> def __init__(self, arg=""): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyschemapy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/schema.py (1326 => 1327)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/schema.py 2006-04-23 22:19:06 UTC (rev 1326) +++ sqlalchemy/branches/schema/lib/sqlalchemy/schema.py 2006-04-23 23:17:53 UTC (rev 1327) </span><span class="lines">@@ -589,6 +589,45 @@ </span><span class="cx"> self.name = name </span><span class="cx"> def is_bound(self): </span><span class="cx"> return False </span><ins>+ + def create_all(self, tables=None, engine=None): + if not tables: + tables = self.tables.values() + + if engine is None and self.is_bound(): + engine = self.engine + + def do(conn): + e = conn.engine + ts = self._sort_tables( tables ) + for table in ts: + if e.dialect.has_table(conn, table.name): + continue + conn.create(table) + engine.run_callable(do) + + def drop_all(self, tables=None, engine=None): + if not tables: + tables = self.tables.values() + + if engine is None and self.is_bound(): + engine = self.engine + + def do(conn): + e = conn.engine + ts = self._sort_tables( tables, reverse=False ) + for table in ts: + if e.dialect.has_table(conn, table.name): + conn.drop(table) + engine.run_callable(do) + + def _sort_tables(self, tables, reverse=True): + import sqlalchemy.sql_util + sorter = sqlalchemy.sql_util.TableCollection() + for t in self.tables.values(): + sorter.add(t) + return sorter.sort(reverse=reverse) + </ins><span class="cx"> def _derived_metadata(self): </span><span class="cx"> return self </span><span class="cx"> def _get_engine(self): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemysql_utilpy"></a> <div class="addfile"><h4>Added: sqlalchemy/branches/schema/lib/sqlalchemy/sql_util.py (1326 => 1327)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/sql_util.py 2006-04-23 22:19:06 UTC (rev 1326) +++ sqlalchemy/branches/schema/lib/sqlalchemy/sql_util.py 2006-04-23 23:17:53 UTC (rev 1327) </span><span class="lines">@@ -0,0 +1,61 @@ </span><ins>+import sqlalchemy.sql as sql +import sqlalchemy.schema as schema + +"""utility functions that build upon SQL and Schema constructs""" + + +class TableCollection(object): + def __init__(self): + self.tables = [] + + def add(self, table): + self.tables.append(table) + + def sort(self, reverse=True ): + import sqlalchemy.orm.topological + tuples = [] + class TVisitor(schema.SchemaVisitor): + def visit_foreign_key(self, fkey): + parent_table = fkey.column.table + child_table = fkey.parent.table + tuples.append( ( child_table, parent_table ) ) + vis = TVisitor() + for table in self.tables: + table.accept_schema_visitor(vis) + sorter = sqlalchemy.orm.topological.QueueDependencySorter( tuples, self.tables ) + head = sorter.sort() + sequence = [] + def to_sequence( node, seq=sequence): + seq.append( node.item ) + for child in node.children: + to_sequence( child ) + to_sequence( head ) + if reverse: + sequence.reverse() + return sequence + + +class TableFinder(TableCollection, sql.ClauseVisitor): + """given a Clause, locates all the Tables within it into a list.""" + def __init__(self, table, check_columns=False): + TableCollection.__init__(self) + self.check_columns = check_columns + if table is not None: + table.accept_visitor(self) + def visit_table(self, table): + self.tables.append(table) + def __len__(self): + return len(self.tables) + def __getitem__(self, i): + return self.tables[i] + def __iter__(self): + return iter(self.tables) + def __contains__(self, obj): + return obj in self.tables + def __add__(self, obj): + return self.tables + list(obj) + def visit_column(self, column): + if self.check_columns: + column.table.accept_visitor(self) + + </ins><span class="cx">\ No newline at end of file </span></span></pre></div> <a id="sqlalchemybranchesschematestreflectionpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/reflection.py (1326 => 1327)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/reflection.py 2006-04-23 22:19:06 UTC (rev 1326) +++ sqlalchemy/branches/schema/test/reflection.py 2006-04-23 23:17:53 UTC (rev 1327) </span><span class="lines">@@ -164,6 +164,58 @@ </span><span class="cx"> self.assert_(isinstance(table.c.col4.type, String)) </span><span class="cx"> finally: </span><span class="cx"> table.drop() </span><ins>+ +class CreateDropTest(PersistTest): + def setUpAll(self): + global metadata + metadata = MetaData() + users = Table('users', metadata, + Column('user_id', Integer, Sequence('user_id_seq', optional=True), primary_key = True), + Column('user_name', String(40)), + ) + + addresses = Table('email_addresses', metadata, + Column('address_id', Integer, Sequence('address_id_seq', optional=True), primary_key = True), + Column('user_id', Integer, ForeignKey(users.c.user_id)), + Column('email_address', String(40)), + + ) + + orders = Table('orders', metadata, + Column('order_id', Integer, Sequence('order_id_seq', optional=True), primary_key = True), + Column('user_id', Integer, ForeignKey(users.c.user_id)), + Column('description', String(50)), + Column('isopen', Integer), + + ) + + orderitems = Table('items', metadata, + Column('item_id', INT, Sequence('items_id_seq', optional=True), primary_key = True), + Column('order_id', INT, ForeignKey("orders")), + Column('item_name', VARCHAR(50)), + + ) + + def test_sorter( self ): + tables = metadata._sort_tables(metadata.tables.values()) + table_names = [t.name for t in tables] + self.assertEqual( table_names, ['users', 'orders', 'items', 'email_addresses'] ) + + + def test_createdrop(self): + metadata.create_all(engine=testbase.db) + self.assertEqual( testbase.db.has_table('items'), True ) + self.assertEqual( testbase.db.has_table('email_addresses'), True ) + metadata.create_all(engine=testbase.db) + self.assertEqual( testbase.db.has_table('items'), True ) + + metadata.drop_all(engine=testbase.db) + self.assertEqual( testbase.db.has_table('items'), False ) + self.assertEqual( testbase.db.has_table('email_addresses'), False ) + metadata.drop_all(engine=testbase.db) + self.assertEqual( testbase.db.has_table('items'), False ) + + </ins><span class="cx"> </span><span class="cx"> if __name__ == "__main__": </span><span class="cx"> testbase.main() </span></span></pre> </div> </div> </body> </html> |