[Sqlalchemy-commits] [1376] sqlalchemy/branches/schema/lib/sqlalchemy: docs, tweaks, gambits DISTINC
Brought to you by:
zzzeek
From: <co...@sq...> - 2006-05-02 21:32:34
|
<!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>[1376] sqlalchemy/branches/schema/lib/sqlalchemy: docs, tweaks, gambits DISTINCT ON pg patch</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1376</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-02 16:32:21 -0500 (Tue, 02 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>docs, tweaks, gambits DISTINCT ON pg patch</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemadocbuildcontentdbenginetxt">sqlalchemy/branches/schema/doc/build/content/dbengine.txt</a></li> <li><a href="#sqlalchemybranchesschemadocbuildcontentmetadatatxt">sqlalchemy/branches/schema/doc/build/content/metadata.txt</a></li> <li><a href="#sqlalchemybranchesschemadocbuildcontentunitofworktxt">sqlalchemy/branches/schema/doc/build/content/unitofwork.txt</a></li> <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="#sqlalchemybranchesschemalibsqlalchemyenginedefaultpy">sqlalchemy/branches/schema/lib/sqlalchemy/engine/default.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormsessionpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormunitofworkpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/unitofwork.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="sqlalchemybranchesschemadocbuildcontentdbenginetxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/dbengine.txt (1375 => 1376)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/dbengine.txt 2006-05-02 21:30:41 UTC (rev 1375) +++ sqlalchemy/branches/schema/doc/build/content/dbengine.txt 2006-05-02 21:32:21 UTC (rev 1376) </span><span class="lines">@@ -26,10 +26,12 @@ </span><span class="cx"> </span><span class="cx"> SQLAlchemy 0.2 indicates the source of an Engine strictly via [RFC-1738](http://rfc.net/rfc1738.html) style URLs, combined with optional keyword arguments to specify options for the Engine. The form of the URL is: </span><span class="cx"> </span><ins>+ {python} </ins><span class="cx"> driver://username:password@host:port/database </span><span class="cx"> </span><span class="cx"> Available drivernames are `sqlite`, `mysql`, `postgres`, `oracle`, `mssql`, and `firebird`. For sqlite, the database name is the filename to connect to, or the special name ":memory:" which indicates an in-memory database. The URL is typically sent as a string to the `create_engine()` function: </span><span class="cx"> </span><ins>+ {python} </ins><span class="cx"> pg_db = create_engine('postgres://scott:tiger@localhost:5432/mydatabase') </span><span class="cx"> sqlite_db = create_engine('sqlite:///mydb.txt') </span><span class="cx"> mysql_db = create_engine('mysql://localhost/foo') </span><span class="lines">@@ -45,7 +47,7 @@ </span><span class="cx"> Options that can be specified include the following: </span><span class="cx"> </span><span class="cx"> * strategy='plain' : the Strategy describes the general configuration used to create this Engine. The two available values are `plain`, which is the default, and `threadlocal`, which applies a "thread-local context" to implicit executions performed by the Engine. This context is further described in the sections below. </span><del>-* pool=None : an instance of `sqlalchemy.pool.Pool` to be used as the underlying source for connections, overriding the engine's connect arguments (pooling is described in [pool](rel:pool)). If None, a default `Pool` (usually `QueuePool`, or `SingletonThreadPool` in the case of SQLite) will be created using the engine's connect arguments. </del><ins>+* pool=None : an instance of `sqlalchemy.pool.Pool` to be used as the underlying source for connections, overriding the engine's connect arguments (pooling is described in [pooling](rel:pooling)). If None, a default `Pool` (usually `QueuePool`, or `SingletonThreadPool` in the case of SQLite) will be created using the engine's connect arguments. </ins><span class="cx"> </span><span class="cx"> Example: </span><span class="cx"> </span><span class="lines">@@ -62,12 +64,10 @@ </span><span class="cx"> * echo=False : if True, the Engine will log all statements as well as a repr() of their parameter lists to the engines logger, which defaults to sys.stdout. A SQLEngine instances' "echo" data member can be modified at any time to turn logging on and off. If set to the string 'debug', result rows will be printed to the standard output as well. </span><span class="cx"> * logger=None : a file-like object where logging output can be sent, if echo is set to True. This defaults to sys.stdout. </span><span class="cx"> * module=None : used by Oracle and Postgres, this is a reference to a DBAPI2 module to be used instead of the engine's default module. For Postgres, the default is psycopg2, or psycopg1 if 2 cannot be found. For Oracle, its cx_Oracle. </span><del>-* default_ordering=False : if True, table objects and associated joins and aliases will generate information used for ordering by primary keys (or OIDs, if the database supports OIDs). This information is used by the Mapper system to when it constructs select queries to supply a default ordering to mapped objects. </del><span class="cx"> * use_ansi=True : used only by Oracle; when False, the Oracle driver attempts to support a particular "quirk" of some Oracle databases, that the LEFT OUTER JOIN SQL syntax is not supported, and the "Oracle join" syntax of using &lt;column1&gt;(+)=&lt;column2&gt; must be used in order to achieve a LEFT OUTER JOIN. Its advised that the Oracle database be configured to have full ANSI support instead of using this feature. </span><span class="cx"> * use_oids=False : used only by Postgres, will enable the column name "oid" as the object ID column. Postgres as of 8.1 has object IDs disabled by default. </span><span class="cx"> * convert_unicode=False : if set to True, all String/character based types will convert Unicode values to raw byte values going into the database, and all raw byte values to Python Unicode coming out in result sets. This is an engine-wide method to provide unicode across the board. For unicode conversion on a column-by-column level, use the Unicode column type instead. </span><span class="cx"> * encoding='utf-8' : the encoding to use for Unicode translations - passed to all encode/decode methods. </span><del>-* echo_uow=False : when True, logs unit of work commit plans to the standard output. </del><span class="cx"> </span><span class="cx"> ### Using Connections {@name=connections} </span><span class="cx"> </span><span class="lines">@@ -84,7 +84,7 @@ </span><span class="cx"> connection.close() </span><span class="cx"> </span><span class="cx"> The `close` method on `Connection` does not actually remove the underlying connection to the database, but rather indicates that the underlying resources can be returned to the connection pool. When using the `connect()` method, the DBAPI connection referenced by the `Connection` object is not referenced anywhere else. </span><del>- </del><ins>+ </ins><span class="cx"> {python title="Implicit Connection"} </span><span class="cx"> engine = create_engine('sqlite:///:memory:') </span><span class="cx"> result = engine.execute("select * from mytable where col1=:col1", {'col1':5}) </span><span class="lines">@@ -105,6 +105,22 @@ </span><span class="cx"> print row['col1'], row['col2'] </span><span class="cx"> connection.close() </span><span class="cx"> </span><ins>+In many cases, the `Connection` and `Engine` can be used interchangeably; as they both provide an `engine` attribute as well as similar `execute` methods, most SQLAlchemy functions which take an `Engine` as a parameter with which to execute SQL will also accept a `Connection`: + + {python title="Specify Engine or Connection"} + engine = create_engine('sqlite:///:memory:') + + # specify some Table metadata + metadata = MetaData() + table = Table('sometable', metadata, Column('col1', Integer)) + + # create the table with the Engine + table.create(engine=engine) + + # drop the table with a Connection off the Engine + connection = engine.connect() + table.drop(engine=connection) + </ins><span class="cx"> #### Implicit Connection Contexts {@name=context} </span><span class="cx"> </span><span class="cx"> "Implicit" connections refer to the example above when the `execute()` method is called directly off the `Engine` object, *without* the usage of a `Connection` object, and resources are released by calling the `close()` method on the result object. When using "implicit" connections, the user has two choices, determined when the Engine is first created, as to how the resources of this connection should be used in relation to other connections. This is determined by the `strategy` argument to `create_engine()`, which has two possible values: `plain` and `threadlocal`. In `plain`, every `execute` call uses a distinct connection from the database, which is only released when the `close()` method on the Result is called. In `threadlocal`, multiple calls to `execute()` within the same thread will use the already-checked out connection resource if one is available, or if none is available will request a connection resource. </span></span></pre></div> <a id="sqlalchemybranchesschemadocbuildcontentmetadatatxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/metadata.txt (1375 => 1376)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/metadata.txt 2006-05-02 21:30:41 UTC (rev 1375) +++ sqlalchemy/branches/schema/doc/build/content/metadata.txt 2006-05-02 21:32:21 UTC (rev 1376) </span><span class="lines">@@ -1,34 +1,48 @@ </span><span class="cx"> Database Meta Data {@name=metadata} </span><span class="cx"> ================== </span><span class="cx"> </span><del>-### Describing Tables with MetaData {@name=tables} </del><ins>+### Describing Databases with MetaData {@name=tables} </ins><span class="cx"> </span><del>-The core of SQLAlchemy's query and object mapping operations is table metadata, which are Python objects that describe tables. Metadata objects can be created by explicitly naming the table and all its properties, using the Table, Column, ForeignKey, and Sequence objects imported from `sqlalchemy.schema`, and a database engine constructed as described in the previous section, or they can be automatically pulled from an existing database schema. First, the explicit version: </del><ins>+The core of SQLAlchemy's query and object mapping operations is database metadata, which are Python objects that describe tables and other schema-level objects. Metadata objects can be created by explicitly naming the various components and their properties, using the Table, Column, ForeignKey, Index, and Sequence objects imported from `sqlalchemy.schema`. There is also support for *reflection*, which means you only specify the *name* of the entities and they are recreated from the database automatically. </ins><span class="cx"> </span><ins>+A collection of metadata entities is stored in an object aptly named `MetaData`. This object takes an optional `name` parameter: + </ins><span class="cx"> {python} </span><span class="cx"> from sqlalchemy import * </span><del>- engine = create_engine('sqlite', {'filename':':memory:'}, **opts) </del><span class="cx"> </span><del>- users = Table('users', engine, </del><ins>+ metadata = MetaData(name='my metadata') + +Then to construct a Table, use the `Table` class: + + {python} + users = Table('users', metadata, </ins><span class="cx"> Column('user_id', Integer, primary_key = True), </span><span class="cx"> Column('user_name', String(16), nullable = False), </span><span class="cx"> Column('email_address', String(60), key='email'), </span><span class="cx"> Column('password', String(20), nullable = False) </span><span class="cx"> ) </span><span class="cx"> </span><del>- user_prefs = Table('user_prefs', engine, </del><ins>+ user_prefs = Table('user_prefs', metadata, </ins><span class="cx"> Column('pref_id', Integer, primary_key=True), </span><span class="cx"> Column('user_id', Integer, ForeignKey("users.user_id"), nullable=False), </span><span class="cx"> Column('pref_name', String(40), nullable=False), </span><span class="cx"> Column('pref_value', String(100)) </span><span class="cx"> ) </span><ins>+ +The specific datatypes for each Column, such as Integer, String, etc. are defined in [types](rel:types) and are automatically pulled in when you import * from `sqlalchemy`. Note that for Column objects, an altername name can be specified via the "key" parameter; if this parameter is given, then all programmatic references to this Column object will be based on its key, instead of its actual column name. + +The `MetaData` object supports some handy methods, such as getting a list of Tables in the order of their dependency: + + {python} + >>> for t in metadata.table_iterator(reverse=True): + ... print t.name + user_prefs + users </ins><span class="cx"> </span><del>-The specific datatypes, such as Integer, String, etc. are defined in [types](rel:types) and are automatically pulled in when you import * from `sqlalchemy`. Note that for Column objects, an altername name can be specified via the "key" parameter; if this parameter is given, then all programmatic references to this Column object will be based on its key, instead of its actual column name. </del><ins>+And `Table` provides an interface to the table's properties as well as that of its columns: </ins><span class="cx"> </span><del>-Once constructed, the Table object provides a clean interface to the table's properties as well as that of its columns: - </del><span class="cx"> {python} </span><del>- employees = Table('employees', engine, </del><ins>+ employees = Table('employees', metadata, </ins><span class="cx"> Column('employee_id', Integer, primary_key=True), </span><span class="cx"> Column('employee_name', String(60), nullable=False, key='name'), </span><span class="cx"> Column('employee_dept', Integer, ForeignKey("departments.department_id")) </span><span class="lines">@@ -55,7 +69,10 @@ </span><span class="cx"> for fkey in employees.foreign_keys: </span><span class="cx"> # ... </span><span class="cx"> </span><del>- # access the table's SQLEngine object: </del><ins>+ # access the table's MetaData: + employees.metadata + + # access the table's Engine, if its MetaData is bound: </ins><span class="cx"> employees.engine </span><span class="cx"> </span><span class="cx"> # access a column's name, type, nullable, primary key, foreign key </span><span class="lines">@@ -75,49 +92,96 @@ </span><span class="cx"> </span><span class="cx"> # get the table related by a foreign key </span><span class="cx"> fcolumn = employees.c.employee_dept.foreign_key.column.table </span><del>- -Metadata objects can also be <b>reflected</b> from tables that already exist in the database. Reflection means based on a table name, the names, datatypes, and attributes of all columns, including foreign keys, will be loaded automatically. This feature is supported by all database engines: </del><span class="cx"> </span><ins>+#### Binding MetaData to an Engine + +A MetaData object can be associated with one or more Engine instances. This allows the MetaData and the elements within it to perform operations automatically, using the connection resources of that Engine. This includes being able to "reflect" the columns of tables, as well as to perform create and drop operations without needing to pass an Engine around. It also allows SQL constructs to be created which know how to execute themselves (called "implicit execution"). + +To bind `MetaData` to a single `Engine`, use `BoundMetaData`: + </ins><span class="cx"> {python} </span><del>- >>> messages = Table('messages', engine, autoload = True) </del><ins>+ engine = create_engine('sqlite://', **kwargs) + + # create BoundMetaData from an Engine + meta = BoundMetaData(engine) + + # create the Engine and MetaData in one step + meta = BoundMetaData('postgres://db/', **kwargs) + +Another form of `MetaData` exists which allows connecting to any number of engines, within the context of the current thread. This is `DynamicMetaData`: + + {python} + meta = DynamicMetaData() + + meta.connect(engine) # connect to an existing Engine + + meta.connect('mysql://user@host/dsn') # create a new Engine and connect + +#### Reflecting Tables + +Once you have a `BoundMetaData` or a connected `DynamicMetaData`, you can create `Table` objects without specifying their columns, just their names, using `autoload=True`: + + {python} + >>> messages = Table('messages', meta, autoload = True) </ins><span class="cx"> >>> [c.name for c in messages.columns] </span><span class="cx"> ['message_id', 'message_name', 'date'] </span><del>- </del><ins>+ +At the moment the Table is constructed, it will query the database for the columns and constraints of the `mytable` table. + </ins><span class="cx"> Note that if a reflected table has a foreign key referencing another table, then the metadata for the related table will be loaded as well, even if it has not been defined by the application: </span><span class="cx"> </span><span class="cx"> {python} </span><del>- >>> shopping_cart_items = Table('shopping_cart_items', engine, autoload = True) </del><ins>+ >>> shopping_cart_items = Table('shopping_cart_items', meta, autoload = True) </ins><span class="cx"> >>> print shopping_cart_items.c.cart_id.table.name </span><span class="cx"> shopping_carts </span><span class="cx"> </span><span class="cx"> To get direct access to 'shopping_carts', simply instantiate it via the Table constructor. You'll get the same instance of the shopping cart Table as the one that is attached to shopping_cart_items: </span><span class="cx"> </span><span class="cx"> {python} </span><del>- >>> shopping_carts = Table('shopping_carts', engine) </del><ins>+ >>> shopping_carts = Table('shopping_carts', meta) </ins><span class="cx"> >>> shopping_carts is shopping_cart_items.c.cart_id.table.name </span><span class="cx"> True </span><span class="cx"> </span><del>-This works because when the Table constructor is called for a particular name and database engine, if the table has already been created then the instance returned will be the same as the original. This is a <b>singleton</b> constructor: </del><ins>+This works because when the Table constructor is called for a particular name and `MetaData` object, if the table has already been created then the instance returned will be the same as the original. This is a <b>singleton</b> constructor: </ins><span class="cx"> </span><span class="cx"> {python} </span><del>- >>> news_articles = Table('news', engine, </del><ins>+ >>> news_articles = Table('news', meta, </ins><span class="cx"> ... Column('article_id', Integer, primary_key = True), </span><span class="cx"> ... Column('url', String(250), nullable = False) </span><span class="cx"> ... ) </span><del>- >>> othertable = Table('news', engine) </del><ins>+ >>> othertable = Table('news', meta) </ins><span class="cx"> >>> othertable is news_articles </span><span class="cx"> True </span><span class="cx"> </span><span class="cx"> ### Creating and Dropping Database Tables {@name=creating} </span><span class="cx"> </span><del>-Creating and dropping is easy, just use the `create()` and `drop()` methods: </del><ins>+Creating and dropping individual tables can be done via a `Connection`: </ins><span class="cx"> </span><span class="cx"> {python} </span><del>- employees = Table('employees', engine, </del><ins>+ employees = Table('employees', meta, </ins><span class="cx"> Column('employee_id', Integer, primary_key=True), </span><span class="cx"> Column('employee_name', String(60), nullable=False, key='name'), </span><span class="cx"> Column('employee_dept', Integer, ForeignKey("departments.department_id")) </span><span class="cx"> ) </span><ins>+ + conn = engine.connect() + {sql}conn.create(employees) + CREATE TABLE employees( + employee_id SERIAL NOT NULL PRIMARY KEY, + employee_name VARCHAR(60) NOT NULL, + employee_dept INTEGER REFERENCES departments(department_id) + ) + {} + +Or off the `create()` method of the `Table` itself; this method takes an optional `engine` parameter which references an `Engine` or a `Connection`. If not supplied, the `Engine` bound to the `MetaData` will be used, else an error is raised: + + {python} + meta = BoundMetaData('sqlite:///:memory:') + employees = Table('employees', meta, + Column('employee_id', Integer, primary_key=True), + Column('employee_name', String(60), nullable=False, key='name'), + Column('employee_dept', Integer, ForeignKey("departments.department_id")) + ) </ins><span class="cx"> {sql}employees.create() </span><span class="cx"> CREATE TABLE employees( </span><span class="cx"> employee_id SERIAL NOT NULL PRIMARY KEY, </span><span class="lines">@@ -125,11 +189,52 @@ </span><span class="cx"> employee_dept INTEGER REFERENCES departments(department_id) </span><span class="cx"> ) </span><span class="cx"> {} </span><ins>+ +Similarly, both `Connection` and `Table` have a `drop()` method: </ins><span class="cx"> </span><del>- {sql}employees.drop() </del><ins>+ {sql}employees.drop(engine=e) </ins><span class="cx"> DROP TABLE employees </span><span class="cx"> {} </span><ins>+ +Entire groups of Tables can be created and dropped directly from the `MetaData` object with `create_all()` and `drop_all()`, each of which take an optional `engine` keyword argument which can reference an `Engine` or a `Connection`, else the underlying bound `Engine` is used: + + {python} + engine = create_engine('sqlite:///:memory:') </ins><span class="cx"> </span><ins>+ metadata = MetaData() + + users = Table('users', metadata, + Column('user_id', Integer, primary_key = True), + Column('user_name', String(16), nullable = False), + Column('email_address', String(60), key='email'), + Column('password', String(20), nullable = False) + ) + + user_prefs = Table('user_prefs', metadata, + Column('pref_id', Integer, primary_key=True), + Column('user_id', Integer, ForeignKey("users.user_id"), nullable=False), + Column('pref_name', String(40), nullable=False), + Column('pref_value', String(100)) + ) + + {sql}metadata.create_all(engine=engine) + PRAGMA table_info(users){} + CREATE TABLE users( + user_id INTEGER NOT NULL PRIMARY KEY, + user_name VARCHAR(16) NOT NULL, + email_address VARCHAR(60), + password VARCHAR(20) NOT NULL + ) + + PRAGMA table_info(user_prefs){} + CREATE TABLE user_prefs( + pref_id INTEGER NOT NULL PRIMARY KEY, + user_id INTEGER NOT NULL REFERENCES users(user_id), + pref_name VARCHAR(40) NOT NULL, + pref_value VARCHAR(100) + ) + + </ins><span class="cx"> ### Column Defaults and OnUpdates {@name=defaults} </span><span class="cx"> </span><span class="cx"> SQLAlchemy includes flexible constructs in which to create default values for columns upon the insertion of rows, as well as upon update. These defaults can take several forms: a constant, a Python callable to be pre-executed before the SQL is executed, a SQL expression or function to be pre-executed before the SQL is executed, a pre-executed Sequence (for databases that support sequences), or a "passive" default, which is a default function triggered by the database itself upon insert, the value of which can then be post-fetched by the engine, provided the row provides a primary key in which to call upon. </span><span class="lines">@@ -146,7 +251,7 @@ </span><span class="cx"> i += 1 </span><span class="cx"> return i </span><span class="cx"> </span><del>- t = Table("mytable", db, </del><ins>+ t = Table("mytable", meta, </ins><span class="cx"> # function-based default </span><span class="cx"> Column('id', Integer, primary_key=True, default=mydefault), </span><span class="cx"> </span><span class="lines">@@ -157,7 +262,7 @@ </span><span class="cx"> The "default" keyword can also take SQL expressions, including select statements or direct function calls: </span><span class="cx"> </span><span class="cx"> {python} </span><del>- t = Table("mytable", db, </del><ins>+ t = Table("mytable", meta, </ins><span class="cx"> Column('id', Integer, primary_key=True), </span><span class="cx"> </span><span class="cx"> # define 'create_date' to default to now() </span><span class="lines">@@ -177,7 +282,7 @@ </span><span class="cx"> Similar to an on-insert default is an on-update default, which is most easily specified by the "onupdate" keyword to Column, which also can be a constant, plain Python function or SQL expression: </span><span class="cx"> </span><span class="cx"> {python} </span><del>- t = Table("mytable", db, </del><ins>+ t = Table("mytable", meta, </ins><span class="cx"> Column('id', Integer, primary_key=True), </span><span class="cx"> </span><span class="cx"> # define 'last_updated' to be populated with current_timestamp (the ANSI-SQL version of now()) </span><span class="lines">@@ -195,7 +300,7 @@ </span><span class="cx"> A PassiveDefault indicates a column default or on-update value that is executed automatically by the database. This construct is used to specify a SQL function that will be specified as "DEFAULT" when creating tables, and also to indicate the presence of new data that is available to be "post-fetched" after an insert or update execution. </span><span class="cx"> </span><span class="cx"> {python} </span><del>- t = Table('test', e, </del><ins>+ t = Table('test', meta, </ins><span class="cx"> Column('mycolumn', DateTime, PassiveDefault("sysdate")) </span><span class="cx"> ) </span><span class="cx"> </span><span class="lines">@@ -220,16 +325,16 @@ </span><span class="cx"> Column('data2', Integer, PassiveDefault("d2_func", for_update=True)) </span><span class="cx"> ) </span><span class="cx"> # insert a row </span><del>- mytable.insert().execute(name='fred') </del><ins>+ r = mytable.insert().execute(name='fred') </ins><span class="cx"> </span><del>- # ask the engine: were there defaults fired off on that row ? - if table.engine.lastrow_has_defaults(): </del><ins>+ # check the result: were there defaults fired off on that row ? + if r.lastrow_has_defaults(): </ins><span class="cx"> # postfetch the row based on primary key. </span><span class="cx"> # this only works for a table with primary key columns defined </span><del>- primary_key = table.engine.last_inserted_ids() </del><ins>+ primary_key = r.last_inserted_ids() </ins><span class="cx"> row = table.select(table.c.id == primary_key[0]) </span><span class="cx"> </span><del>-When Tables are reflected from the database using <code>autoload=True</code>, any DEFAULT values set on the columns will be reflected in the Table object as PassiveDefault instances. </del><ins>+When Tables are reflected from the database using `autoload=True`, any DEFAULT values set on the columns will be reflected in the Table object as PassiveDefault instances. </ins><span class="cx"> </span><span class="cx"> ##### The Catch: Postgres Primary Key Defaults always Pre-Execute {@name=postgres} </span><span class="cx"> </span><span class="lines">@@ -241,7 +346,7 @@ </span><span class="cx"> A table with a sequence looks like: </span><span class="cx"> </span><span class="cx"> {python} </span><del>- table = Table("cartitems", db, </del><ins>+ table = Table("cartitems", meta, </ins><span class="cx"> Column("cart_id", Integer, Sequence('cart_id_seq'), primary_key=True), </span><span class="cx"> Column("description", String(40)), </span><span class="cx"> Column("createdate", DateTime()) </span><span class="lines">@@ -249,7 +354,7 @@ </span><span class="cx"> </span><span class="cx"> The Sequence is used with Postgres or Oracle to indicate the name of a Sequence that will be used to create default values for a column. When a table with a Sequence on a column is created by SQLAlchemy, the Sequence object is also created. Similarly, the Sequence is dropped when the table is dropped. Sequences are typically used with primary key columns. When using Postgres, if an integer primary key column defines no explicit Sequence or other default method, SQLAlchemy will create the column with the SERIAL keyword, and will pre-execute a sequence named "tablename_columnname_seq" in order to retrieve new primary key values. Oracle, which has no "auto-increment" keyword, requires that a Sequence be created for a table if automatic primary key generation is desired. Note that for all databases, primary key values can always be explicitly stated within the bind parameters for any insert statement as well, removing the need! for any kind of default generation function. </span><span class="cx"> </span><del>-A Sequence object can be defined on a Table that is then used for a non-sequence-supporting database. In that case, the Sequence object is simply ignored. Note that a Sequence object is <b>entirely optional for all databases except Oracle</b>, as other databases offer options for auto-creating primary key values, such as AUTOINCREMENT, SERIAL, etc. SQLAlchemy will use these default methods for creating primary key values if no Sequence is present on the table metadata. </del><ins>+A Sequence object can be defined on a Table that is then used for a non-sequence-supporting database. In that case, the Sequence object is simply ignored. Note that a Sequence object is **entirely optional for all databases except Oracle**, as other databases offer options for auto-creating primary key values, such as AUTOINCREMENT, SERIAL, etc. SQLAlchemy will use these default methods for creating primary key values if no Sequence is present on the table metadata. </ins><span class="cx"> </span><span class="cx"> A sequence can also be specified with `optional=True` which indicates the Sequence should only be used on a database that requires an explicit sequence, and not those that supply some other method of providing integer values. At the moment, it essentially means "use this sequence only with Oracle and not Postgres". </span><span class="cx"> </span><span class="lines">@@ -258,7 +363,8 @@ </span><span class="cx"> Indexes can be defined on table columns, including named indexes, non-unique or unique, multiple column. Indexes are included along with table create and drop statements. They are not used for any kind of run-time constraint checking; SQLAlchemy leaves that job to the expert on constraint checking, the database itself. </span><span class="cx"> </span><span class="cx"> {python} </span><del>- mytable = Table('mytable', engine, </del><ins>+ boundmeta = BoundMetaData('postgres:///scott:tiger@localhost/test') + mytable = Table('mytable', boundmeta, </ins><span class="cx"> # define a unique index </span><span class="cx"> Column('col1', Integer, unique=True), </span><span class="cx"> </span><span class="lines">@@ -287,50 +393,19 @@ </span><span class="cx"> # which can then be created separately (will also get created with table creates) </span><span class="cx"> i.create() </span><span class="cx"> </span><del>-### Adapting Tables to Alternate Engines {@name=adapting} </del><ins>+### Adapting Tables to Alternate Metadata {@name=adapting} </ins><span class="cx"> </span><del>-A Table object created against a specific engine can be re-created against a new engine using the `toengine` method: </del><ins>+A `Table` object created against a specific `MetaData` object can be re-created against a new engine using the `tometadata` method: </ins><span class="cx"> </span><span class="cx"> {python} </span><del>- # create two engines - sqlite_engine = create_engine('sqlite', {'filename':'querytest.db'}) - postgres_engine = create_engine('postgres', - {'database':'test', - 'host':'127.0.0.1', 'user':'scott', 'password':'tiger'}) </del><ins>+ # create two metadata + meta1 = BoundMetaData('sqlite:///querytest.db') + meta2 = MetaData() </ins><span class="cx"> </span><span class="cx"> # load 'users' from the sqlite engine </span><del>- users = Table('users', sqlite_engine, autoload=True) </del><ins>+ users_table = Table('users', meta1, autoload=True) </ins><span class="cx"> </span><del>- # create the same Table object for the other engine - pg_users = users.toengine(postgres_engine) </del><ins>+ # create the same Table object for the plain metadata + users_table_2 = users.tometadata(meta2) </ins><span class="cx"> </span><del>-Also available is the "database neutral" ansisql engine: - - {python} - import sqlalchemy.ansisql as ansisql - generic_engine = ansisql.engine() </del><span class="cx"> </span><del>- users = Table('users', generic_engine, - Column('user_id', Integer), - Column('user_name', String(50)) - ) - -Flexible "multi-engined" tables can also be achieved via the proxy engine, described in the section [dbengine_proxy](rel:dbengine_proxy). - -#### Non-engine primitives: TableClause/ColumnClause {@name=primitives} - -TableClause and ColumnClause are "primitive" versions of the Table and Column objects which dont use engines at all; applications that just want to generate SQL strings but not directly communicate with a database can use TableClause and ColumnClause objects (accessed via 'table' and 'column'), which are non-singleton and serve as the "lexical" base class of Table and Column: - - {python} - tab1 = table('table1', - column('id'), - column('name')) - - tab2 = table('table2', - column('id'), - column('email')) - - tab1.select(tab1.c.name == 'foo') - -TableClause and ColumnClause are strictly lexical. This means they are fully supported within the full range of SQL statement generation, but they don't support schema concepts like creates, drops, primary keys, defaults, nullable status, indexes, or foreign keys. - </del></span></pre></div> <a id="sqlalchemybranchesschemadocbuildcontentunitofworktxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/unitofwork.txt (1375 => 1376)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/unitofwork.txt 2006-05-02 21:30:41 UTC (rev 1375) +++ sqlalchemy/branches/schema/doc/build/content/unitofwork.txt 2006-05-02 21:32:21 UTC (rev 1376) </span><span class="lines">@@ -13,7 +13,7 @@ </span><span class="cx"> * an Identity Map, which is a dictionary storing the one and only instance of an object for a particular table/primary key combination. This allows many parts of an application to get a handle to a particular object without any chance of modifications going to two different places. </span><span class="cx"> * The sole interface to the unit of work is provided via the `Session` object. Transactional capability, which rides on top of the transactions provided by `Engine` objects, is provided by the `SessionTransaction` object. </span><span class="cx"> * Thread-locally scoped Session behavior is available as an option, which allows new objects to be automatically added to the Session corresponding to by the *default Session context*. Without a default Session context, an application must explicitly create a Session manually as well as add new objects to it. The default Session context, disabled by default, can also be plugged in with other user-defined schemes, which may also take into account the specific class being dealt with for a particular operation. </span><del>-* The Session object in SQLAlchemy 0.2 borrows conceptually from that of [hibernate](http://www.hibernate.org), which is a leading ORM for Java. SQLAlchemy is in general very different from Hibernate, providing a different paradigm for producing queries, a SQL API that is useable independently of the ORM, and of course Pythonic configuration as opposed to XML; however, Hibernate has covered the bases pretty well with its Session ideology. </del><ins>+* The Session object in SQLAlchemy 0.2 borrows conceptually from that of [Hibernate](http://www.hibernate.org), a leading ORM for Java that is largely based on [JSR-220](http://jcp.org/aboutJava/communityprocess/pfd/jsr220/index.html). SQLAlchemy, under no obligation to conform to EJB specifications, is in general very different from Hibernate, providing a different paradigm for producing queries, a SQL API that is useable independently of the ORM, and of course Pythonic configuration as opposed to XML; however, JSR-220/Hibernate makes some pretty good suggestions with regards to the mechanisms of persistence. </ins><span class="cx"> </span><span class="cx"> ### Object States {@name=states} </span><span class="cx"> </span><span class="lines">@@ -140,6 +140,18 @@ </span><span class="cx"> </span><span class="cx"> The `query()` function takes a class or `Mapper` as an argument, along with an optional `entity_name` parameter, and returns a new `Query` object which will issue mapper queries within the context of this Session. If a Mapper is passed, then the Query uses that mapper. Otherwise, if a class is sent, it will locate the primary mapper for that class which is used to construct the Query. </span><span class="cx"> </span><ins>+ {python} + # query from a class + session.query(User).select_by(name='ed') + + # query from a mapper + query = session.query(usermapper) + x = query.get(1) + + # query from a class mapped with entity name 'alt_users' + q = session.query(User, entity_name='alt_users') + y = q.options(eagerload('orders')).select() + </ins><span class="cx"> "entity_name" is an optional keyword argument sent with a class object, in order to further qualify which primary mapper to be used; this only applies if there was a `Mapper` created with that particular class/entity name combination, else an exception is raised. All of the methods on Session which take a class or mapper argument also take the "entity_name" argument, so that a given class can be properly matched to the desired primary mapper. </span><span class="cx"> </span><span class="cx"> All instances retrieved by the returned `Query` object will be stored as persistent instances within the originating Session. </span><span class="lines">@@ -148,10 +160,25 @@ </span><span class="cx"> </span><span class="cx"> Given a class or mapper, a scalar or tuple-based identity, and an optional "entity_name" keyword argument, creates a `Query` corresponding to the given mapper or class/entity_name combination, and calls the `get()` method with the given identity value. If the object already exists within this Session, it is simply returned, else it is queried from the database. If the instance is not found, the method returns `None`. </span><span class="cx"> </span><ins>+ {python} + # get Employer primary key 5 + employer = session.get(Employer, 5) + + # get Report composite primary key 7,12, using mapper 'report_mapper_b' + report = session.get(Report, (7,12), entity_name='report_mapper_b') + + </ins><span class="cx"> #### load() {@name=load} </span><span class="cx"> </span><span class="cx"> load() is similar to get() except it will raise an exception if the instance does not exist in the database. It will also load the object's data from the database in all cases, and **overwrite** all changes on the object if it already exists in the session with the latest data from the database. </span><span class="cx"> </span><ins>+ {python} + # load Employer primary key 5 + employer = session.load(Employer, 5) + + # load Report composite primary key 7,12, using mapper 'report_mapper_b' + report = session.load(Report, (7,12), entity_name='report_mapper_b') + </ins><span class="cx"> #### save() {@name=save} </span><span class="cx"> </span><span class="cx"> save() is called with a single transient (unsaved, unattached) instance as an argument, which is then added to the Session and becomes pending. When the session is next `flush()`ed, the instance will be saved to the database uponwhich it becomes persistent (saved, attached). If the given instance is not transient, meaning it is either attached to an existing Session or it has a database identity, an exception is raised. </span><span class="lines">@@ -162,6 +189,14 @@ </span><span class="cx"> </span><span class="cx"> The `save_or_update()` method is a convenience method which will call the `save()` or `update()` methods appropriately dependening on whether or not the instance has a database identity (but the instance still must be unattached). </span><span class="cx"> </span><ins>+ {python} + user1 = User(name='user1') + user2 = User(name='user2') + session.save(user1) + session.save(user2) + + session.flush() # write changes to the database + </ins><span class="cx"> #### flush() {@name=flush} </span><span class="cx"> </span><span class="cx"> This is the main gateway to what the Unit of Work does best, which is save everything ! It should be clear by now that a flush looks like: </span><span class="lines">@@ -321,6 +356,8 @@ </span><span class="cx"> </span><span class="cx"> `SessionTransaction`, like the `Transaction` off of `Connection` also supports "nested" behavior, and is safe to pass to other functions which then issue their own `begin()`/`commit()` pair; only the outermost `begin()`/`commit()` pair actually affects the transaction, and any call to `rollback()` within a particular call stack will issue a rollback. </span><span class="cx"> </span><ins>+Note that while SessionTransaction is capable of tracking multiple transactions across multiple databases, it currently is in no way a fully functioning two-phase commit engine; generally, when dealing with multiple databases simultaneously, there is the distinct possibility that a transaction can succeed on the first database and fail on the second, which for some applications may be an invalid state. If this is an issue, its best to either refrain from spanning transactions across databases, or to look into some of the available technologies in this area, such as [Zope](http://www.zope.org) which offers a two-phase commit engine; some users have already created their own SQLAlchemy/Zope hybrid implementations to deal with scenarios like these. + </ins><span class="cx"> #### Using SQL with SessionTransaction {@name=sql} </span><span class="cx"> </span><span class="cx"> The SessionTransaction can interact with direct SQL queries in two general ways. Either specific `Connection` objects can be associated with the `SessionTransaction`, which are then useable both for direct SQL as well as within `flush()` operations performed by the `SessionTransaction`, or via accessing the `Connection` object automatically referenced within the `SessionTransaction`. </span><span class="lines">@@ -339,7 +376,7 @@ </span><span class="cx"> trans.rollback() </span><span class="cx"> raise </span><span class="cx"> </span><del>-The `add()` method will key the `Connection`'s underlying `Engine` to this `SessionTransaction`. When mapper operations are performed against this `Engine`, the `Connection` explicitly added will be used. This **overrides** any other `Connection` objects that the Session was associated with, corresponding to the underlying `Engine` of that `Connection`. </del><ins>+The `add()` method will key the `Connection`'s underlying `Engine` to this `SessionTransaction`. When mapper operations are performed against this `Engine`, the `Connection` explicitly added will be used. This **overrides** any other `Connection` objects that the underlying Session was associated with, corresponding to the underlying `Engine` of that `Connection`. However, if the `SessionTransaction` itself is already associated with a `Connection`, then an exception is thrown. </ins><span class="cx"> </span><span class="cx"> The other way is just to use the `Connection` referenced by the `SessionTransaction`. This is performed via the `connection()` method, and requires passing in a `Mapper` which indicates which underlying `Connection` should be returned. If the `Mapper` argument is `None`, then the `Session` must be globally bound to a specific `Engine` when it was constructed, else the method returns `None`. </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemydatabasespostgrespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py (1375 => 1376)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py 2006-05-02 21:30:41 UTC (rev 1375) +++ sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py 2006-05-02 21:32:21 UTC (rev 1376) </span><span class="lines">@@ -306,6 +306,20 @@ </span><span class="cx"> text += " OFFSET " + str(select.offset) </span><span class="cx"> return text </span><span class="cx"> </span><ins>+ def visit_select_precolumns(self, select): + if select.distinct: + if type(select.distinct) == bool: + return "DISTINCT " + if type(select.distinct) == list: + dist_set = "DISTINCT ON (" + for col in select.distinct: + dist_set += self.strings[col] + ", " + dist_set = dist_set[:-2] + ") " + return dist_set + return "DISTINCT ON (" + str(select.distinct) + ") " + else: + return "" + </ins><span class="cx"> def binary_operator_string(self, binary): </span><span class="cx"> if isinstance(binary.type, sqltypes.String) and binary.operator == '+': </span><span class="cx"> return '||' </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyenginebasepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py (1375 => 1376)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py 2006-05-02 21:30:41 UTC (rev 1375) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py 2006-05-02 21:32:21 UTC (rev 1376) </span><span class="lines">@@ -331,12 +331,10 @@ </span><span class="cx"> provide a default implementation of SchemaEngine. </span><span class="cx"> """ </span><span class="cx"> </span><del>- def __init__(self, connection_provider, dialect, echo=False, logger=None, echo_uow=False, **kwargs): </del><ins>+ def __init__(self, connection_provider, dialect, echo=False, logger=None, **kwargs): </ins><span class="cx"> self.connection_provider = connection_provider </span><span class="cx"> self.dialect=dialect </span><span class="cx"> self.echo = echo </span><del>- # TODO: echo_uow should be only in Session - self.echo_uow = echo_uow </del><span class="cx"> self.logger = logger or util.Logger(origin='engine') </span><span class="cx"> </span><span class="cx"> def _get_name(self): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyenginedefaultpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/engine/default.py (1375 => 1376)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/default.py 2006-05-02 21:30:41 UTC (rev 1375) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/default.py 2006-05-02 21:32:21 UTC (rev 1376) </span><span class="lines">@@ -39,8 +39,7 @@ </span><span class="cx"> </span><span class="cx"> class DefaultDialect(base.Dialect): </span><span class="cx"> """default implementation of Dialect""" </span><del>- def __init__(self, default_ordering=False, convert_unicode=False, encoding='utf-8', **kwargs): - self.default_ordering=default_ordering </del><ins>+ def __init__(self, convert_unicode=False, encoding='utf-8', **kwargs): </ins><span class="cx"> self.convert_unicode = convert_unicode </span><span class="cx"> self.supports_autoclose_results = True </span><span class="cx"> self.encoding = encoding </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormsessionpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py (1375 => 1376)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py 2006-05-02 21:30:41 UTC (rev 1375) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py 2006-05-02 21:32:21 UTC (rev 1376) </span><span class="lines">@@ -67,7 +67,7 @@ </span><span class="cx"> </span><span class="cx"> class Session(object): </span><span class="cx"> """encapsulates a set of objects being operated upon within an object-relational operation.""" </span><del>- def __init__(self, bind_to=None, hash_key=None, new_imap=True, import_session=None): </del><ins>+ def __init__(self, bind_to=None, hash_key=None, new_imap=True, import_session=None, echo_uow=False): </ins><span class="cx"> if import_session is not None: </span><span class="cx"> self.uow = unitofwork.UnitOfWork(identity_map=import_session.uow.identity_map) </span><span class="cx"> elif new_imap is False: </span><span class="lines">@@ -77,6 +77,7 @@ </span><span class="cx"> </span><span class="cx"> self.bind_to = bind_to </span><span class="cx"> self.binds = {} </span><ins>+ self.echo_uow = echo_uow </ins><span class="cx"> self.transaction = None </span><span class="cx"> if hash_key is None: </span><span class="cx"> self.hash_key = id(self) </span><span class="lines">@@ -229,7 +230,7 @@ </span><span class="cx"> def flush(self, objects=None): </span><span class="cx"> """flushes all the object modifications present in this session to the database. 'objects' </span><span class="cx"> is a list or tuple of objects specifically to be flushed.""" </span><del>- self.uow.flush(self, objects) </del><ins>+ self.uow.flush(self, objects, echo=self.echo_uow) </ins><span class="cx"> </span><span class="cx"> def get(self, class_, ident, **kwargs): </span><span class="cx"> """returns an instance of the object based on the given identifier, or None </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormunitofworkpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/unitofwork.py (1375 => 1376)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/unitofwork.py 2006-05-02 21:30:41 UTC (rev 1375) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/unitofwork.py 2006-05-02 21:32:21 UTC (rev 1376) </span><span class="lines">@@ -214,7 +214,7 @@ </span><span class="cx"> except KeyError: </span><span class="cx"> pass </span><span class="cx"> </span><del>- def flush(self, session, objects=None): </del><ins>+ def flush(self, session, objects=None, echo=False): </ins><span class="cx"> flush_context = UOWTransaction(self, session) </span><span class="cx"> </span><span class="cx"> if objects is not None: </span><span class="lines">@@ -236,9 +236,8 @@ </span><span class="cx"> </span><span class="cx"> trans = session.create_transaction(autoflush=False) </span><span class="cx"> flush_context.transaction = trans </span><del>- echo_commit = False </del><span class="cx"> try: </span><del>- flush_context.execute(echo=echo_commit) </del><ins>+ flush_context.execute(echo=echo) </ins><span class="cx"> trans.commit() </span><span class="cx"> except: </span><span class="cx"> trans.rollback() </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyschemapy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/schema.py (1375 => 1376)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/schema.py 2006-05-02 21:30:41 UTC (rev 1375) +++ sqlalchemy/branches/schema/lib/sqlalchemy/schema.py 2006-05-02 21:32:21 UTC (rev 1376) </span><span class="lines">@@ -217,11 +217,17 @@ </span><span class="cx"> issue a SQL DROP statement.""" </span><span class="cx"> key = _get_table_key(self.name, self.schema) </span><span class="cx"> del self.metadata.tables[key] </span><del>- def create(self, **params): - self.engine.create(self) </del><ins>+ def create(self, engine=None): + if engine is not None: + engine.create(self) + else: + self.engine.create(self) </ins><span class="cx"> return self </span><del>- def drop(self, **params): - self.engine.drop(self) </del><ins>+ def drop(self, engine=None): + if engine is not None: + engine.drop(self) + else: + self.engine.drop(self) </ins><span class="cx"> def tometadata(self, metadata, schema=None): </span><span class="cx"> """returns a singleton instance of this Table with a different Schema""" </span><span class="cx"> try: </span><span class="lines">@@ -569,13 +575,17 @@ </span><span class="cx"> % (self.name, column)) </span><span class="cx"> self.columns.append(column) </span><span class="cx"> </span><del>- def create(self): - self.engine.create(self) - return self - def drop(self): - self.engine.drop(self) - def execute(self): - self.create() </del><ins>+ def create(self, engine=None): + if engine is not None: + engine.create(self) + else: + self.engine.create(self) + return self + def drop(self, engine=None): + if engine is not None: + engine.drop(self) + else: + self.engine.drop(self) </ins><span class="cx"> def accept_schema_visitor(self, visitor): </span><span class="cx"> visitor.visit_index(self) </span><span class="cx"> def __str__(self): </span></span></pre> </div> </div> </body> </html> |