sqlalchemy-commits Mailing List for SQLAlchemy (Page 365)
Brought to you by:
zzzeek
You can subscribe to this list here.
2006 |
Jan
|
Feb
(74) |
Mar
(167) |
Apr
(127) |
May
(190) |
Jun
(119) |
Jul
(77) |
Aug
(82) |
Sep
(84) |
Oct
(153) |
Nov
(45) |
Dec
(54) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2007 |
Jan
(109) |
Feb
(80) |
Mar
(110) |
Apr
(106) |
May
(92) |
Jun
(147) |
Jul
(288) |
Aug
(307) |
Sep
(108) |
Oct
(156) |
Nov
(147) |
Dec
(134) |
2008 |
Jan
(126) |
Feb
(91) |
Mar
(184) |
Apr
(208) |
May
(212) |
Jun
(54) |
Jul
(106) |
Aug
(80) |
Sep
(58) |
Oct
(80) |
Nov
(119) |
Dec
(220) |
2009 |
Jan
(202) |
Feb
(50) |
Mar
(70) |
Apr
(46) |
May
(80) |
Jun
(61) |
Jul
(146) |
Aug
(81) |
Sep
(71) |
Oct
(74) |
Nov
(66) |
Dec
(82) |
2010 |
Jan
(112) |
Feb
(169) |
Mar
(235) |
Apr
(77) |
May
(22) |
Jun
(31) |
Jul
(46) |
Aug
(46) |
Sep
(70) |
Oct
(36) |
Nov
(37) |
Dec
(79) |
2011 |
Jan
(46) |
Feb
(54) |
Mar
(65) |
Apr
(73) |
May
(31) |
Jun
(46) |
Jul
(40) |
Aug
(36) |
Sep
(44) |
Oct
(33) |
Nov
(19) |
Dec
(10) |
2012 |
Jan
(60) |
Feb
(37) |
Mar
(35) |
Apr
(28) |
May
(27) |
Jun
(50) |
Jul
(33) |
Aug
(88) |
Sep
(64) |
Oct
(74) |
Nov
(62) |
Dec
(41) |
2013 |
Jan
(30) |
Feb
(37) |
Mar
(39) |
Apr
(52) |
May
(40) |
Jun
(85) |
Jul
(74) |
Aug
(76) |
Sep
(26) |
Oct
(76) |
Nov
(63) |
Dec
(65) |
2014 |
Jan
(68) |
Feb
(82) |
Mar
(87) |
Apr
(24) |
May
(66) |
Jun
(34) |
Jul
(86) |
Aug
(75) |
Sep
(70) |
Oct
(41) |
Nov
(23) |
Dec
(53) |
2015 |
Jan
(40) |
Feb
(39) |
Mar
(69) |
Apr
(64) |
May
(40) |
Jun
(43) |
Jul
(20) |
Aug
(48) |
Sep
(38) |
Oct
(28) |
Nov
(34) |
Dec
(44) |
2016 |
Jan
(82) |
Feb
(49) |
Mar
(25) |
Apr
(21) |
May
(19) |
Jun
(46) |
Jul
(38) |
Aug
(21) |
Sep
(33) |
Oct
(44) |
Nov
(26) |
Dec
(10) |
2017 |
Jan
(52) |
Feb
(18) |
Mar
(61) |
Apr
(43) |
May
(57) |
Jun
(36) |
Jul
(37) |
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
From: <co...@sq...> - 2006-04-25 05:44:21
|
<!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>[1336] sqlalchemy/branches/schema/lib/sqlalchemy/ext/sessioncontext.py: initial checkin for dan miller's SessionContext implementation</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1336</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-25 00:44:11 -0500 (Tue, 25 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>initial checkin for dan miller's SessionContext implementation</pre> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyextsessioncontextpy">sqlalchemy/branches/schema/lib/sqlalchemy/ext/sessioncontext.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemyextsessioncontextpy"></a> <div class="addfile"><h4>Added: sqlalchemy/branches/schema/lib/sqlalchemy/ext/sessioncontext.py (1335 => 1336)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/ext/sessioncontext.py 2006-04-25 01:57:51 UTC (rev 1335) +++ sqlalchemy/branches/schema/lib/sqlalchemy/ext/sessioncontext.py 2006-04-25 05:44:11 UTC (rev 1336) </span><span class="lines">@@ -0,0 +1,118 @@ </span><ins>+from sqlalchemy.util import ScopedRegistry + +class SessionContext(object): + """A simple wrapper for ScopedRegistry that provides a "current" property + which can be used to get, set, or remove the session in the current scope. + + By default this object provides thread-local scoping, which is the default + scope provided by sqlalchemy.util.ScopedRegistry. + + Usage: + engine = create_engine(...) + def session_factory(): + return Session(bind_to=engine) + context = SessionContext(session_factory) + + s = context.current # get thread-local session + context.current = Session(bind_to=other_engine) # set current session + del context.current # discard the thread-local session (a new one will + # be created on the next call to context.current) + """ + def __init__(self, session_factory, scopefunc=None): + self.registry = ScopedRegistry(session_factory, scopefunc) + super(SessionContext, self).__init__() + + def get_current(self): + return self.registry() + def set_current(self, session): + self.registry.set(session) + def del_current(self): + self.registry.clear() + current = property(get_current, set_current, del_current, + """Property used to get/set/del the session in the current scope""") + + def create_metaclass(session_context): + """return a metaclass to be used by objects that wish to be bound to a + thread-local session upon instantiatoin. + + Note non-standard use of session_context rather than self as the name + of the first arguement of this method. + + Usage: + context = SessionContext(...) + class MyClass(object): + __metaclass__ = context.metaclass + ... + """ + try: + return session_context._metaclass + except AttributeError: + class metaclass(type): + def __init__(cls, name, bases, dct): + old_init = getattr(cls, "__init__") + def __init__(self, *args, **kwargs): + session_context.current.save(self) + old_init(self, *args, **kwargs) + setattr(cls, "__init__", __init__) + super(metaclass, cls).__init__(name, bases, dct) + session_context._metaclass = metaclass + return metaclass + metaclass = property(create_metaclass) + + def create_baseclass(session_context): + """return a baseclass to be used by objects that wish to be bound to a + thread-local session upon instantiatoin. + + Note non-standard use of session_context rather than self as the name + of the first arguement of this method. + + Usage: + context = SessionContext(...) + class MyClass(context.baseclass): + ... + """ + try: + return session_context._baseclass + except AttributeError: + class baseclass(object): + def __init__(self, *args, **kwargs): + session_context.current.save(self) + super(baseclass, self).__init__(*args, **kwargs) + session_context._baseclass = baseclass + return baseclass + baseclass = property(create_baseclass) + + +def test(): + + def run_test(class_, context): + obj = class_() + assert context.current == get_session(obj) + + # keep a reference so the old session doesn't get gc'd + old_session = context.current + + context.current = create_session() + assert context.current != get_session(obj) + assert old_session == get_session(obj) + + del context.current + assert context.current != get_session(obj) + assert old_session == get_session(obj) + + obj2 = class_() + assert context.current == get_session(obj2) + + # test metaclass + context = SessionContext(create_session) + class MyClass(object): __metaclass__ = context.metaclass + run_test(MyClass, context) + + # test baseclass + context = SessionContext(create_session) + class MyClass(context.baseclass): pass + run_test(MyClass, context) + +if __name__ == "__main__": + test() + print "All tests passed!" </ins></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-25 01:58:12
|
<!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>[1335] sqlalchemy/branches/schema/lib/sqlalchemy: added URL class, various fixes/tweaks, changes to contextual_connection</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1335</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-24 20:57:51 -0500 (Mon, 24 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>added URL class, various fixes/tweaks, changes to contextual_connection</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemydatabasesmysqlpy">sqlalchemy/branches/schema/lib/sqlalchemy/databases/mysql.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemydatabasespostgrespy">sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemydatabasessqlitepy">sqlalchemy/branches/schema/lib/sqlalchemy/databases/sqlite.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyengine__init__py">sqlalchemy/branches/schema/lib/sqlalchemy/engine/__init__.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="#sqlalchemybranchesschemalibsqlalchemyenginestrategiespy">sqlalchemy/branches/schema/lib/sqlalchemy/engine/strategies.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> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyengineurlpy">sqlalchemy/branches/schema/lib/sqlalchemy/engine/url.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemydatabasesmysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/databases/mysql.py (1334 => 1335)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/databases/mysql.py 2006-04-24 22:11:13 UTC (rev 1334) +++ sqlalchemy/branches/schema/lib/sqlalchemy/databases/mysql.py 2006-04-25 01:57:51 UTC (rev 1335) </span><span class="lines">@@ -124,9 +124,9 @@ </span><span class="cx"> return {'name':'mysql', </span><span class="cx"> 'description':'MySQL', </span><span class="cx"> 'arguments':[ </span><del>- ('user',"Database Username",None), - ('passwd',"Database Password",None), - ('db',"Database Name",None), </del><ins>+ ('username',"Database Username",None), + ('password',"Database Password",None), + ('database',"Database Name",None), </ins><span class="cx"> ('host',"Hostname", None), </span><span class="cx"> ]} </span><span class="cx"> </span><span class="lines">@@ -142,8 +142,8 @@ </span><span class="cx"> self.module = mysql </span><span class="cx"> ansisql.ANSIDialect.__init__(self, **kwargs) </span><span class="cx"> </span><del>- def create_connect_args(self, opts): - opts = self._translate_connect_args(('host', 'db', 'user', 'passwd'), opts) </del><ins>+ def create_connect_args(self, url): + opts = url.translate_connect_args(['host', 'db', 'user', 'passwd', 'port']) </ins><span class="cx"> return [[], opts] </span><span class="cx"> </span><span class="cx"> def create_execution_context(self): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemydatabasespostgrespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py (1334 => 1335)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py 2006-04-24 22:11:13 UTC (rev 1334) +++ sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py 2006-04-25 01:57:51 UTC (rev 1335) </span><span class="lines">@@ -174,7 +174,7 @@ </span><span class="cx"> return {'name':'postgres', </span><span class="cx"> 'description':'PostGres', </span><span class="cx"> 'arguments':[ </span><del>- ('user',"Database Username",None), </del><ins>+ ('username',"Database Username",None), </ins><span class="cx"> ('password',"Database Password",None), </span><span class="cx"> ('database',"Database Name",None), </span><span class="cx"> ('host',"Hostname", None), </span><span class="lines">@@ -216,8 +216,8 @@ </span><span class="cx"> self.version = 1 </span><span class="cx"> ansisql.ANSIDialect.__init__(self, **params) </span><span class="cx"> </span><del>- def create_connect_args(self, opts): - opts = self._translate_connect_args(('host', 'database', 'user', 'password'), opts) </del><ins>+ def create_connect_args(self, url): + opts = url.translate_connect_args(['host', 'database', 'user', 'password', 'port']) </ins><span class="cx"> if opts.has_key('port'): </span><span class="cx"> if self.version == 2: </span><span class="cx"> opts['port'] = int(opts['port']) </span><span class="lines">@@ -275,9 +275,6 @@ </span><span class="cx"> return self.module </span><span class="cx"> </span><span class="cx"> def has_table(self, connection, table_name): </span><del>- """ - return boolean whether or not the engine/schema contains this table - """ </del><span class="cx"> cursor = connection.execute("""select relname from pg_class where relname = %(name)s""", {'name':table_name}) </span><span class="cx"> return bool( not not cursor.rowcount ) </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemydatabasessqlitepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/databases/sqlite.py (1334 => 1335)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/databases/sqlite.py 2006-04-24 22:11:13 UTC (rev 1334) +++ sqlalchemy/branches/schema/lib/sqlalchemy/databases/sqlite.py 2006-04-25 01:57:51 UTC (rev 1335) </span><span class="lines">@@ -116,7 +116,7 @@ </span><span class="cx"> return {'name':'sqlite', </span><span class="cx"> 'description':'SQLite', </span><span class="cx"> 'arguments':[ </span><del>- ('filename', "Database Filename",None) </del><ins>+ ('host', "Database Filename",None) </ins><span class="cx"> ]} </span><span class="cx"> </span><span class="cx"> </span><span class="lines">@@ -130,8 +130,8 @@ </span><span class="cx"> return SQLiteCompiler(self, statement, bindparams, **kwargs) </span><span class="cx"> def schemagenerator(self, *args, **kwargs): </span><span class="cx"> return SQLiteSchemaGenerator(*args, **kwargs) </span><del>- def create_connect_args(self, opts): - filename = opts.pop('filename', ':memory:') </del><ins>+ def create_connect_args(self, url): + filename = url.host or ':memory:' </ins><span class="cx"> return ([filename], {}) </span><span class="cx"> def type_descriptor(self, typeobj): </span><span class="cx"> return sqltypes.adapt_type(typeobj, colspecs) </span><span class="lines">@@ -154,6 +154,10 @@ </span><span class="cx"> def push_session(self): </span><span class="cx"> raise InvalidRequestError("SQLite doesn't support nested sessions") </span><span class="cx"> </span><ins>+ def has_table(self, connection, table_name): + cursor = connection.execute("PRAGMA table_info(" + table_name + ")", {}) + return bool(cursor.rowcount>0) + </ins><span class="cx"> def reflecttable(self, connection, table): </span><span class="cx"> c = connection.execute("PRAGMA table_info(" + table.name + ")", {}) </span><span class="cx"> while True: </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyengine__init__py"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/engine/__init__.py (1334 => 1335)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/__init__.py 2006-04-24 22:11:13 UTC (rev 1334) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/__init__.py 2006-04-25 01:57:51 UTC (rev 1335) </span><span class="lines">@@ -26,7 +26,8 @@ </span><span class="cx"> query the user for database and connection information. </span><span class="cx"> """ </span><span class="cx"> result = [] </span><del>- for module in sqlalchemy.databases.__all__: </del><ins>+ #for module in sqlalchemy.databases.__all__: + for module in ['sqlite', 'postgres', 'mysql']: </ins><span class="cx"> module = getattr(__import__('sqlalchemy.databases.%s' % module).databases, module) </span><span class="cx"> result.append(module.descriptor()) </span><span class="cx"> return result </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyenginebasepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py (1334 => 1335)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py 2006-04-24 22:11:13 UTC (rev 1334) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py 2006-04-25 01:57:51 UTC (rev 1335) </span><span class="lines">@@ -166,7 +166,7 @@ </span><span class="cx"> def connect(self): </span><span class="cx"> """connect() is implemented to return self so that an incoming Engine or Connection object can be treated similarly.""" </span><span class="cx"> return self </span><del>- def contextual_connect(self): </del><ins>+ def contextual_connect(self, **kwargs): </ins><span class="cx"> """contextual_connect() is implemented to return self so that an incoming Engine or Connection object can be treated similarly.""" </span><span class="cx"> return self </span><span class="cx"> def begin(self): </span><span class="lines">@@ -182,8 +182,9 @@ </span><span class="cx"> if self.transaction is None and re.match(r'UPDATE|INSERT|CREATE|DELETE|DROP', statement.lstrip().upper()): </span><span class="cx"> self.engine.dialect.do_commit(self.connection) </span><span class="cx"> def close(self): </span><del>- self.connection.close() - self.connection = None </del><ins>+ if self.connection is not None: + self.connection.close() + self.connection = None </ins><span class="cx"> def scalar(self, object, parameters, **kwargs): </span><span class="cx"> row = self.execute(object, parameters, **kwargs).fetchone() </span><span class="cx"> if row is not None: </span><span class="lines">@@ -363,7 +364,7 @@ </span><span class="cx"> </span><span class="cx"> def _run_visitor(self, visitorcallable, element, connection=None, **kwargs): </span><span class="cx"> if connection is None: </span><del>- conn = self.contextual_connect() </del><ins>+ conn = self.contextual_connect(close_with_result=False) </ins><span class="cx"> else: </span><span class="cx"> conn = connection </span><span class="cx"> try: </span><span class="lines">@@ -374,7 +375,7 @@ </span><span class="cx"> </span><span class="cx"> def run_callable(self, callable_, connection=None): </span><span class="cx"> if connection is None: </span><del>- conn = self.contextual_connect() </del><ins>+ conn = self.contextual_connect(close_with_result=False) </ins><span class="cx"> else: </span><span class="cx"> conn = connection </span><span class="cx"> try: </span><span class="lines">@@ -384,11 +385,11 @@ </span><span class="cx"> conn.close() </span><span class="cx"> </span><span class="cx"> def execute(self, *args, **kwargs): </span><del>- connection = self.contextual_connect() </del><ins>+ connection = self.contextual_connect(close_with_result=True) </ins><span class="cx"> return connection.execute(*args, **kwargs) </span><span class="cx"> </span><span class="cx"> def execute_compiled(self, compiled, parameters, **kwargs): </span><del>- connection = self.contextual_connect() </del><ins>+ connection = self.contextual_connect(close_with_result=True) </ins><span class="cx"> return connection.execute_compiled(compiled, parameters, **kwargs) </span><span class="cx"> </span><span class="cx"> def compiler(self, statement, parameters, **kwargs): </span><span class="lines">@@ -398,15 +399,15 @@ </span><span class="cx"> """returns a newly allocated Connection object.""" </span><span class="cx"> return Connection(self, **kwargs) </span><span class="cx"> </span><del>- def contextual_connect(self, **kwargs): </del><ins>+ def contextual_connect(self, close_with_result=False, **kwargs): </ins><span class="cx"> """returns a Connection object which may be newly allocated, or may be part of some </span><span class="cx"> ongoing context. This Connection is meant to be used by the various "auto-connecting" operations.""" </span><del>- return Connection(self, close_with_result=True, **kwargs) </del><ins>+ return Connection(self, close_with_result=close_with_result, **kwargs) </ins><span class="cx"> </span><span class="cx"> def reflecttable(self, table, connection=None): </span><span class="cx"> """given a Table object, reflects its columns and properties from the database.""" </span><span class="cx"> if connection is None: </span><del>- conn = self.contextual_connect() </del><ins>+ conn = self.contextual_connect(close_with_result=False) </ins><span class="cx"> else: </span><span class="cx"> conn = connection </span><span class="cx"> try: </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyenginedefaultpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/engine/default.py (1334 => 1335)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/default.py 2006-04-24 22:11:13 UTC (rev 1334) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/default.py 2006-04-25 01:57:51 UTC (rev 1335) </span><span class="lines">@@ -14,8 +14,8 @@ </span><span class="cx"> </span><span class="cx"> </span><span class="cx"> class PoolConnectionProvider(base.ConnectionProvider): </span><del>- def __init__(self, dialect, connectopts, poolclass=None, pool=None, **kwargs): - (cargs, cparams) = dialect.create_connect_args(connectopts) </del><ins>+ def __init__(self, dialect, url, poolclass=None, pool=None, **kwargs): + (cargs, cparams) = dialect.create_connect_args(url) </ins><span class="cx"> if pool is None: </span><span class="cx"> kwargs.setdefault('echo', False) </span><span class="cx"> kwargs.setdefault('use_threadlocal',True) </span><span class="lines">@@ -80,25 +80,6 @@ </span><span class="cx"> cursor.execute(statement, parameters) </span><span class="cx"> def defaultrunner(self, engine, proxy): </span><span class="cx"> return base.DefaultRunner(engine, proxy) </span><del>- def _translate_connect_args(self, names, args): - """translates a dictionary of connection arguments to those used by a specific dbapi. - the names parameter is a tuple of argument names in the form ('host', 'database', 'user', 'password') - where the given strings match the corresponding argument names for the dbapi. Will return a dictionary - with the dbapi-specific parameters, the generic ones removed, and any additional parameters still remaining, - from the dictionary represented by args. Will return a blank dictionary if args is null.""" - if args is None: - return {} - a = {} #args.copy() - standard_names = [('host','hostname'), ('database', 'dbname'), ('user', 'username'), ('password', 'passwd', 'pw')] - for n in names: - sname = standard_names.pop(0) - if n is None: - continue - for sn in sname: - if args.has_key(sn): - a[n] = args[sn] - #del a[sn] - return a </del><span class="cx"> </span><span class="cx"> def _set_paramstyle(self, style): </span><span class="cx"> self._paramstyle = style </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyenginestrategiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/engine/strategies.py (1334 => 1335)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/strategies.py 2006-04-24 22:11:13 UTC (rev 1334) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/strategies.py 2006-04-25 01:57:51 UTC (rev 1335) </span><span class="lines">@@ -4,10 +4,8 @@ </span><span class="cx"> list of available strategies here, or replace one of the existing name. </span><span class="cx"> this can be accomplished via a mod; see the sqlalchemy/mods package for details.""" </span><span class="cx"> </span><del>-import re -import cgi </del><span class="cx"> </span><del>-from sqlalchemy.engine import base, default, threadlocal </del><ins>+from sqlalchemy.engine import base, default, threadlocal, url </ins><span class="cx"> </span><span class="cx"> strategies = {} </span><span class="cx"> </span><span class="lines">@@ -27,8 +25,9 @@ </span><span class="cx"> class PlainEngineStrategy(EngineStrategy): </span><span class="cx"> def __init__(self): </span><span class="cx"> EngineStrategy.__init__(self, 'plain') </span><del>- def create(self, name, opts=None, **kwargs): - (module, opts) = _parse_db_args(name, opts) </del><ins>+ def create(self, name_or_url, **kwargs): + u = url.make_url(name_or_url) + module = u.get_module() </ins><span class="cx"> </span><span class="cx"> dialect = module.dialect(**kwargs) </span><span class="cx"> </span><span class="lines">@@ -38,7 +37,7 @@ </span><span class="cx"> if poolclass is not None: </span><span class="cx"> poolargs.setdefault('poolclass', poolclass) </span><span class="cx"> poolargs['use_threadlocal'] = False </span><del>- provider = default.PoolConnectionProvider(dialect, opts, **poolargs) </del><ins>+ provider = default.PoolConnectionProvider(dialect, u, **poolargs) </ins><span class="cx"> </span><span class="cx"> return base.ComposedSQLEngine(provider, dialect, **kwargs) </span><span class="cx"> PlainEngineStrategy() </span><span class="lines">@@ -46,8 +45,9 @@ </span><span class="cx"> class ThreadLocalEngineStrategy(EngineStrategy): </span><span class="cx"> def __init__(self): </span><span class="cx"> EngineStrategy.__init__(self, 'threadlocal') </span><del>- def create(self, name, opts=None, **kwargs): - (module, opts) = _parse_db_args(name, opts) </del><ins>+ def create(self, name_or_url, **kwargs): + u = url.make_url(name_or_url) + module = u.get_module() </ins><span class="cx"> </span><span class="cx"> dialect = module.dialect(**kwargs) </span><span class="cx"> </span><span class="lines">@@ -57,52 +57,10 @@ </span><span class="cx"> if poolclass is not None: </span><span class="cx"> poolargs.setdefault('poolclass', poolclass) </span><span class="cx"> poolargs['use_threadlocal'] = True </span><del>- provider = threadlocal.TLocalConnectionProvider(dialect, opts, **poolargs) </del><ins>+ provider = threadlocal.TLocalConnectionProvider(dialect, u, **poolargs) </ins><span class="cx"> </span><span class="cx"> return threadlocal.TLEngine(provider, dialect, **kwargs) </span><span class="cx"> ThreadLocalEngineStrategy() </span><span class="cx"> </span><span class="cx"> </span><del>-def _parse_db_args(name, opts=None): - ret = _parse_rfc1738_args(name, opts=opts) - #if ret is None: - # ret = _parse_keyvalue_args(name, opts=opts) - if ret is not None: - (name, opts) = ret - - module = getattr(__import__('sqlalchemy.databases.%s' % name).databases, name) - return (module, opts) - -def _parse_rfc1738_args(name, opts=None): - pattern = re.compile(r''' - (\w+):// - (?: - ([^:]*) - (?::(.*))? - @)? - (?: - ([^/:]*) - (?::([^/]*))? - ) - (?:/(.*))? - ''' - , re.X) </del><span class="cx"> </span><del>- m = pattern.match(name) - if m is not None and (m.group(4) or m.group(6)): - (name, username, password, host, port, database) = m.group(1, 2, 3, 4, 5, 6) - opts = {'username':username,'password':password,'host':host,'port':port,'database':database,'filename':(database or host)} - return (name, opts) - else: - return None - -def _parse_keyvalue_args(name, opts=None): - m = re.match( r'(\w+)://(.*)', name) - if m is not None: - (name, args) = m.group(1, 2) - opts = dict( cgi.parse_qsl( args ) ) - return (name, opts) - else: - return None - - </del></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyengineurlpy"></a> <div class="addfile"><h4>Added: sqlalchemy/branches/schema/lib/sqlalchemy/engine/url.py (1334 => 1335)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/url.py 2006-04-24 22:11:13 UTC (rev 1334) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/url.py 2006-04-25 01:57:51 UTC (rev 1335) </span><span class="lines">@@ -0,0 +1,67 @@ </span><ins>+import re +import cgi + +class URL(object): + def __init__(self, drivername, username=None, password=None, host=None, port=None, database=None): + self.drivername = drivername + self.username = username + self.password = password + self.host = host + self.port = port + self.database= database + def get_module(self): + return getattr(__import__('sqlalchemy.databases.%s' % self.drivername).databases, self.drivername) + def translate_connect_args(self, names): + """translates this URL's attributes into a dictionary of connection arguments used by a specific dbapi. + the names parameter is a list of argument names in the form ('host', 'database', 'user', 'password', 'port') + where the given strings match the corresponding argument names for the dbapi. Will return a dictionary + with the dbapi-specific parameters.""" + a = {} + attribute_names = ['host', 'database', 'username', 'password', 'port'] + for n in names: + sname = attribute_names.pop(0) + if n is None: + continue + if getattr(self, sname, None) is not None: + a[n] = getattr(self, sname) + return a + + +def make_url(name_or_url): + if isinstance(name_or_url, str): + return _parse_rfc1738_args(name_or_url) + else: + return name_or_url + +def _parse_rfc1738_args(name): + pattern = re.compile(r''' + (\w+):// + (?: + ([^:]*) + (?::(.*))? + @)? + (?: + ([^/:]*) + (?::([^/]*))? + ) + (?:/(.*))? + ''' + , re.X) + + m = pattern.match(name) + if m is not None and (m.group(4) or m.group(6)): + (name, username, password, host, port, database) = m.group(1, 2, 3, 4, 5, 6) + opts = {'username':username,'password':password,'host':host,'port':port,'database':database} + return URL(name, **opts) + else: + return None + +def _parse_keyvalue_args(name): + m = re.match( r'(\w+)://(.*)', name) + if m is not None: + (name, args) = m.group(1, 2) + opts = dict( cgi.parse_qsl( args ) ) + return URL(name, *opts) + else: + return None + </ins></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormsessionpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py (1334 => 1335)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py 2006-04-24 22:11:13 UTC (rev 1334) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py 2006-04-25 01:57:51 UTC (rev 1335) </span><span class="lines">@@ -105,7 +105,7 @@ </span><span class="cx"> to the execution's results. If this method allocates a new Connection for the operation, then the ResultProxy's close() </span><span class="cx"> method will release the resources of the underlying Connection, otherwise its a no-op. </span><span class="cx"> """ </span><del>- return self.connection(mapper).execute(clause, params, **kwargs) </del><ins>+ return self.connection(mapper, close_with_result=True).execute(clause, params, **kwargs) </ins><span class="cx"> def close(self): </span><span class="cx"> """closes this Session. </span><span class="cx"> </span><span class="lines">@@ -223,12 +223,12 @@ </span><span class="cx"> def refresh(self, object): </span><span class="cx"> """reloads the attributes for the given object from the database, clears </span><span class="cx"> any changes made.""" </span><del>- self.uow.refresh(object) </del><ins>+ self.uow.refresh(self, object) </ins><span class="cx"> </span><span class="cx"> def expire(self, object): </span><span class="cx"> """invalidates the data in the given object and sets them to refresh themselves </span><span class="cx"> the next time they are requested.""" </span><del>- self.uow.expire(object) </del><ins>+ self.uow.expire(self, object) </ins><span class="cx"> </span><span class="cx"> def expunge(self, object): </span><span class="cx"> """removes the given object from this Session. this will free all internal references to the object.""" </span><span class="lines">@@ -271,6 +271,7 @@ </span><span class="cx"> self.uow.register_deleted(c) </span><span class="cx"> </span><span class="cx"> def merge(self, object, entity_name=None): </span><ins>+ instance = None </ins><span class="cx"> for obj in object_mapper(object, entity_name=entity_name).cascade_iterator('merge', object): </span><span class="cx"> key = getattr(obj, '_instance_key', None) </span><span class="cx"> if key is None: </span><span class="lines">@@ -282,12 +283,14 @@ </span><span class="cx"> key = mapper.identity_key(*ident) </span><span class="cx"> u = self.uow </span><span class="cx"> if u.identity_map.has_key(key): </span><del>- return u.identity_map[key] </del><ins>+ # TODO: copy the state of the given object into this one. tricky ! + inst = u.identity_map[key] </ins><span class="cx"> else: </span><del>- obj._instance_key = key - u.identity_map[key] = obj - self._bind_to(instance) - return instance </del><ins>+ inst = self.get(*key[1]) + if obj is object: + instance = inst + + return instance </ins><span class="cx"> </span><span class="cx"> def _save_impl(self, object, **kwargs): </span><span class="cx"> if hasattr(object, '_instance_key'): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormunitofworkpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/unitofwork.py (1334 => 1335)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/unitofwork.py 2006-04-24 22:11:13 UTC (rev 1334) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/unitofwork.py 2006-04-25 01:57:51 UTC (rev 1335) </span><span class="lines">@@ -108,14 +108,14 @@ </span><span class="cx"> def _put(self, key, obj): </span><span class="cx"> self.identity_map[key] = obj </span><span class="cx"> </span><del>- def refresh(self, obj): </del><ins>+ def refresh(self, sess, obj): </ins><span class="cx"> self.rollback_object(obj) </span><del>- object_mapper(obj)._get(obj._instance_key, reload=True) </del><ins>+ sess.query(obj.__class__)._get(obj._instance_key, reload=True) </ins><span class="cx"> </span><del>- def expire(self, obj): </del><ins>+ def expire(self, sess, obj): </ins><span class="cx"> self.rollback_object(obj) </span><span class="cx"> def exp(): </span><del>- object_mapper(obj)._get(obj._instance_key, reload=True) </del><ins>+ sess.query(obj.__class__)._get(obj._instance_key, reload=True) </ins><span class="cx"> global_attributes.trigger_history(obj, exp) </span><span class="cx"> </span><span class="cx"> def is_expired(self, obj, unexpire=False): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyschemapy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/schema.py (1334 => 1335)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/schema.py 2006-04-24 22:11:13 UTC (rev 1334) +++ sqlalchemy/branches/schema/lib/sqlalchemy/schema.py 2006-04-25 01:57:51 UTC (rev 1335) </span><span class="lines">@@ -426,7 +426,7 @@ </span><span class="cx"> schema = self.parent.original.table.schema </span><span class="cx"> else: </span><span class="cx"> (schema,tname,colname) = m.group(1,2,3) </span><del>- table = Table(tname, self.parent.metadata, mustexist=True, schema=schema) </del><ins>+ table = Table(tname, self.parent.original.metadata, mustexist=True, schema=schema) </ins><span class="cx"> if colname is None: </span><span class="cx"> key = self.parent </span><span class="cx"> self._column = table.c[self.parent.key] </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-24 22:11:26
|
<!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>[1334] sqlalchemy/trunk/test: removed a "swap" from the lazy binary clause.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1334</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-24 17:11:13 -0500 (Mon, 24 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>removed a "swap" from the lazy binary clause. added a test for that condition....</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemymappingpropertiespy">sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py</a></li> <li><a href="#sqlalchemytrunktestlazytest1py">sqlalchemy/trunk/test/lazytest1.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemymappingpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py (1333 => 1334)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-04-24 19:31:23 UTC (rev 1333) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-04-24 22:11:13 UTC (rev 1334) </span><span class="lines">@@ -660,7 +660,7 @@ </span><span class="cx"> binary.left = binds.setdefault(binary.left, </span><span class="cx"> sql.BindParamClause(bind_label(), None, shortname = binary.left.name)) </span><span class="cx"> reverselookup[binary.right] = binds[col] </span><del>- binary.swap() </del><ins>+ #binary.swap() </ins><span class="cx"> </span><span class="cx"> if isinstance(binary.right, schema.Column) and isinstance(binary.left, schema.Column) and ((not circular and binary.right.table is table) or (circular and binary.left is foreignkey)): </span><span class="cx"> col = binary.right </span><span class="lines">@@ -671,7 +671,7 @@ </span><span class="cx"> lazywhere = primaryjoin.copy_container() </span><span class="cx"> li = BinaryVisitor(visit_binary) </span><span class="cx"> lazywhere.accept_visitor(li) </span><del>- #print "PRIMARYJOIN", str(lazywhere), [b.key for b in binds.values()] </del><ins>+ print "PRIMARYJOIN", str(lazywhere), [b.key for b in binds.values()] </ins><span class="cx"> if secondaryjoin is not None: </span><span class="cx"> lazywhere = sql.and_(lazywhere, secondaryjoin) </span><span class="cx"> return (lazywhere, binds, reverselookup) </span></span></pre></div> <a id="sqlalchemytrunktestlazytest1py"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/lazytest1.py (1333 => 1334)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/lazytest1.py 2006-04-24 19:31:23 UTC (rev 1333) +++ sqlalchemy/trunk/test/lazytest1.py 2006-04-24 22:11:13 UTC (rev 1334) </span><span class="lines">@@ -56,6 +56,9 @@ </span><span class="cx"> rel_table.drop() </span><span class="cx"> info_table.drop() </span><span class="cx"> </span><ins>+ def setUp(self): + clear_mappers() + </ins><span class="cx"> def testone(self): </span><span class="cx"> """tests a lazy load which has multiple join conditions, including two that are against </span><span class="cx"> the same column in the child table""" </span><span class="lines">@@ -76,7 +79,8 @@ </span><span class="cx"> Relation.mapper.add_property('datas', relation(Data.mapper, </span><span class="cx"> primaryjoin=and_(Relation.c.info_pk==Data.c.info_pk, </span><span class="cx"> Data.c.timeval >= Relation.c.start, </span><del>- Data.c.timeval <= Relation.c.finish), </del><ins>+ Data.c.timeval <= Relation.c.finish + ), </ins><span class="cx"> foreignkey=Data.c.info_pk)) </span><span class="cx"> </span><span class="cx"> Information.mapper.add_property('rels', relation(Relation.mapper)) </span><span class="lines">@@ -86,6 +90,39 @@ </span><span class="cx"> assert len(info.rels) == 2 </span><span class="cx"> assert len(info.rels[0].datas) == 3 </span><span class="cx"> </span><ins>+ def testtwo(self): + """same thing, but reversing the order of the cols in the join""" + class Information(object): + pass + + class Relation(object): + pass + + class Data(object): + pass + + # Create the basic mappers, with no frills or modifications + Information.mapper = mapper(Information, info_table) + Data.mapper = mapper(Data, data_table) + Relation.mapper = mapper(Relation, rel_table) + + Relation.mapper.add_property('datas', relation(Data.mapper, + primaryjoin=and_(Relation.c.info_pk==Data.c.info_pk, + Relation.c.start <= Data.c.timeval, + Relation.c.finish >= Data.c.timeval, + # Data.c.timeval >= Relation.c.start, + # Data.c.timeval <= Relation.c.finish + ), + foreignkey=Data.c.info_pk)) + + Information.mapper.add_property('rels', relation(Relation.mapper)) + + info = Information.mapper.get(1) + assert info + assert len(info.rels) == 2 + assert len(info.rels[0].datas) == 3 + + </ins><span class="cx"> if __name__ == "__main__": </span><span class="cx"> testbase.main() </span><span class="cx"> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-24 19:31:36
|
<!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>[1333] sqlalchemy/branches/schema/test: merged fix from 1331->1332 into 0.2</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1333</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-24 14:31:23 -0500 (Mon, 24 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>merged fix from 1331->1332 into 0.2</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormpropertiespy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py</a></li> <li><a href="#sqlalchemybranchesschematestalltestspy">sqlalchemy/branches/schema/test/alltests.py</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemybranchesschematestlazytest1py">sqlalchemy/branches/schema/test/lazytest1.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemyormpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py (1332 => 1333)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py 2006-04-24 19:21:12 UTC (rev 1332) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py 2006-04-24 19:31:23 UTC (rev 1333) </span><span class="lines">@@ -20,7 +20,7 @@ </span><span class="cx"> import dependency </span><span class="cx"> import util as mapperutil </span><span class="cx"> from sqlalchemy.exceptions import * </span><del>-import sets </del><ins>+import sets, random </ins><span class="cx"> </span><span class="cx"> class ColumnProperty(MapperProperty): </span><span class="cx"> """describes an object attribute that corresponds to a table column.""" </span><span class="lines">@@ -359,7 +359,7 @@ </span><span class="cx"> </span><span class="cx"> class LazyLoader(PropertyLoader): </span><span class="cx"> def do_init_subclass(self, key, parent): </span><del>- (self.lazywhere, self.lazybinds) = create_lazy_clause(self.parent.noninherited_table, self.primaryjoin, self.secondaryjoin, self.foreignkey) </del><ins>+ (self.lazywhere, self.lazybinds, self.lazyreverse) = create_lazy_clause(self.parent.noninherited_table, self.primaryjoin, self.secondaryjoin, self.foreignkey) </ins><span class="cx"> # determine if our "lazywhere" clause is the same as the mapper's </span><span class="cx"> # get() clause. then we can just use mapper.get() </span><span class="cx"> self.use_get = not self.uselist and self.mapper.query()._get_clause.compare(self.lazywhere) </span><span class="lines">@@ -388,7 +388,8 @@ </span><span class="cx"> if self.use_get: </span><span class="cx"> ident = [] </span><span class="cx"> for primary_key in self.mapper.pks_by_table[self.mapper.table]: </span><del>- ident.append(params[primary_key._label]) </del><ins>+ bind = self.lazyreverse[primary_key] + ident.append(params[bind.key]) </ins><span class="cx"> return self.mapper.using(session).get(*ident) </span><span class="cx"> elif self.order_by is not False: </span><span class="cx"> order_by = self.order_by </span><span class="lines">@@ -426,23 +427,31 @@ </span><span class="cx"> </span><span class="cx"> def create_lazy_clause(table, primaryjoin, secondaryjoin, foreignkey): </span><span class="cx"> binds = {} </span><ins>+ reverse = {} + def bind_label(): + return "lazy_" + hex(random.randint(0, 65535))[2:] + </ins><span class="cx"> def visit_binary(binary): </span><span class="cx"> circular = isinstance(binary.left, schema.Column) and isinstance(binary.right, schema.Column) and binary.left.table is binary.right.table </span><span class="cx"> if isinstance(binary.left, schema.Column) and isinstance(binary.right, schema.Column) and ((not circular and binary.left.table is table) or (circular and binary.right is foreignkey)): </span><ins>+ col = binary.left </ins><span class="cx"> binary.left = binds.setdefault(binary.left, </span><del>- sql.BindParamClause(binary.right._label, None, shortname = binary.left.name)) </del><ins>+ sql.BindParamClause(bind_label(), None, shortname = binary.left.name)) + reverse[binary.right] = binds[col] </ins><span class="cx"> binary.swap() </span><span class="cx"> </span><span class="cx"> if isinstance(binary.right, schema.Column) and isinstance(binary.left, schema.Column) and ((not circular and binary.right.table is table) or (circular and binary.left is foreignkey)): </span><ins>+ col = binary.right </ins><span class="cx"> binary.right = binds.setdefault(binary.right, </span><del>- sql.BindParamClause(binary.left._label, None, shortname = binary.right.name)) - </del><ins>+ sql.BindParamClause(bind_label(), None, shortname = binary.right.name)) + reverse[binary.left] = binds[col] + </ins><span class="cx"> lazywhere = primaryjoin.copy_container() </span><span class="cx"> li = BinaryVisitor(visit_binary) </span><span class="cx"> lazywhere.accept_visitor(li) </span><span class="cx"> if secondaryjoin is not None: </span><span class="cx"> lazywhere = sql.and_(lazywhere, secondaryjoin) </span><del>- return (lazywhere, binds) </del><ins>+ return (lazywhere, binds, reverse) </ins><span class="cx"> </span><span class="cx"> </span><span class="cx"> class EagerLoader(PropertyLoader): </span></span></pre></div> <a id="sqlalchemybranchesschematestalltestspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/alltests.py (1332 => 1333)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/alltests.py 2006-04-24 19:21:12 UTC (rev 1332) +++ sqlalchemy/branches/schema/test/alltests.py 2006-04-24 19:31:23 UTC (rev 1333) </span><span class="lines">@@ -34,6 +34,7 @@ </span><span class="cx"> # ORM selecting </span><span class="cx"> 'mapper', </span><span class="cx"> 'selectresults', </span><ins>+ 'lazytest1', </ins><span class="cx"> 'eagertest1', </span><span class="cx"> 'eagertest2', </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschematestlazytest1pyfromrev1332sqlalchemytrunktestlazytest1py"></a> <div class="copfile"><h4>Copied: sqlalchemy/branches/schema/test/lazytest1.py (from rev 1332, sqlalchemy/trunk/test/lazytest1.py) ( => )</h4> <pre class="diff"><span> <span class="info"> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-24 19:21:25
|
<!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>[1332] sqlalchemy/trunk/test: lazyload clause calculation uses anonymous keynames for the bind parameters, to avoid compilation name conflicts</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1332</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-24 14:21:12 -0500 (Mon, 24 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>lazyload clause calculation uses anonymous keynames for the bind parameters, to avoid compilation name conflicts</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemymappingpropertiespy">sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py</a></li> <li><a href="#sqlalchemytrunktestalltestspy">sqlalchemy/trunk/test/alltests.py</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemytrunktestlazytest1py">sqlalchemy/trunk/test/lazytest1.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemymappingpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py (1331 => 1332)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-04-24 01:17:08 UTC (rev 1331) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-04-24 19:21:12 UTC (rev 1332) </span><span class="lines">@@ -18,6 +18,7 @@ </span><span class="cx"> import mapper </span><span class="cx"> import objectstore </span><span class="cx"> from sqlalchemy.exceptions import * </span><ins>+import random </ins><span class="cx"> </span><span class="cx"> class ColumnProperty(MapperProperty): </span><span class="cx"> """describes an object attribute that corresponds to a table column.""" </span><span class="lines">@@ -579,7 +580,7 @@ </span><span class="cx"> </span><span class="cx"> class LazyLoader(PropertyLoader): </span><span class="cx"> def do_init_subclass(self, key, parent): </span><del>- (self.lazywhere, self.lazybinds) = create_lazy_clause(self.parent.noninherited_table, self.primaryjoin, self.secondaryjoin, self.foreignkey) </del><ins>+ (self.lazywhere, self.lazybinds, self.lazyreverse) = create_lazy_clause(self.parent.noninherited_table, self.primaryjoin, self.secondaryjoin, self.foreignkey) </ins><span class="cx"> # determine if our "lazywhere" clause is the same as the mapper's </span><span class="cx"> # get() clause. then we can just use mapper.get() </span><span class="cx"> self.use_get = not self.uselist and self.mapper.query._get_clause.compare(self.lazywhere) </span><span class="lines">@@ -608,7 +609,8 @@ </span><span class="cx"> if self.use_get: </span><span class="cx"> ident = [] </span><span class="cx"> for primary_key in self.mapper.pks_by_table[self.mapper.table]: </span><del>- ident.append(params[primary_key._label]) </del><ins>+ bind = self.lazyreverse[primary_key] + ident.append(params[bind.key]) </ins><span class="cx"> return self.mapper.using(session).get(*ident) </span><span class="cx"> elif self.order_by is not False: </span><span class="cx"> order_by = self.order_by </span><span class="lines">@@ -646,23 +648,33 @@ </span><span class="cx"> </span><span class="cx"> def create_lazy_clause(table, primaryjoin, secondaryjoin, foreignkey): </span><span class="cx"> binds = {} </span><ins>+ reverselookup = {} + + def bind_label(): + return "lazy_" + hex(random.randint(0, 65535))[2:] + </ins><span class="cx"> def visit_binary(binary): </span><span class="cx"> circular = isinstance(binary.left, schema.Column) and isinstance(binary.right, schema.Column) and binary.left.table is binary.right.table </span><span class="cx"> if isinstance(binary.left, schema.Column) and isinstance(binary.right, schema.Column) and ((not circular and binary.left.table is table) or (circular and binary.right is foreignkey)): </span><ins>+ col = binary.left </ins><span class="cx"> binary.left = binds.setdefault(binary.left, </span><del>- sql.BindParamClause(binary.right._label, None, shortname = binary.left.name)) </del><ins>+ sql.BindParamClause(bind_label(), None, shortname = binary.left.name)) + reverselookup[binary.right] = binds[col] </ins><span class="cx"> binary.swap() </span><span class="cx"> </span><span class="cx"> if isinstance(binary.right, schema.Column) and isinstance(binary.left, schema.Column) and ((not circular and binary.right.table is table) or (circular and binary.left is foreignkey)): </span><ins>+ col = binary.right </ins><span class="cx"> binary.right = binds.setdefault(binary.right, </span><del>- sql.BindParamClause(binary.left._label, None, shortname = binary.right.name)) </del><ins>+ sql.BindParamClause(bind_label(), None, shortname = binary.right.name)) + reverselookup[binary.left] = binds[col] </ins><span class="cx"> </span><span class="cx"> lazywhere = primaryjoin.copy_container() </span><span class="cx"> li = BinaryVisitor(visit_binary) </span><span class="cx"> lazywhere.accept_visitor(li) </span><ins>+ #print "PRIMARYJOIN", str(lazywhere), [b.key for b in binds.values()] </ins><span class="cx"> if secondaryjoin is not None: </span><span class="cx"> lazywhere = sql.and_(lazywhere, secondaryjoin) </span><del>- return (lazywhere, binds) </del><ins>+ return (lazywhere, binds, reverselookup) </ins><span class="cx"> </span><span class="cx"> </span><span class="cx"> class EagerLoader(PropertyLoader): </span></span></pre></div> <a id="sqlalchemytrunktestalltestspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/alltests.py (1331 => 1332)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/alltests.py 2006-04-24 01:17:08 UTC (rev 1331) +++ sqlalchemy/trunk/test/alltests.py 2006-04-24 19:21:12 UTC (rev 1332) </span><span class="lines">@@ -34,6 +34,7 @@ </span><span class="cx"> # ORM selecting </span><span class="cx"> 'mapper', </span><span class="cx"> 'selectresults', </span><ins>+ 'lazytest1', </ins><span class="cx"> 'eagertest1', </span><span class="cx"> 'eagertest2', </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemytrunktestlazytest1py"></a> <div class="addfile"><h4>Added: sqlalchemy/trunk/test/lazytest1.py (1331 => 1332)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/lazytest1.py 2006-04-24 01:17:08 UTC (rev 1331) +++ sqlalchemy/trunk/test/lazytest1.py 2006-04-24 19:21:12 UTC (rev 1332) </span><span class="lines">@@ -0,0 +1,92 @@ </span><ins>+from testbase import PersistTest, AssertMixin +import testbase +import unittest, sys, os +from sqlalchemy import * +import datetime + +class LazyTest(AssertMixin): + def setUpAll(self): + global info_table, data_table, rel_table + engine = testbase.db + info_table = Table('infos', engine, + Column('pk', Integer, primary_key=True), + Column('info', String)) + + data_table = Table('data', engine, + Column('data_pk', Integer, primary_key=True), + Column('info_pk', Integer, ForeignKey(info_table.c.pk)), + Column('timeval', Integer), + Column('data_val', String)) + + rel_table = Table('rels', engine, + Column('rel_pk', Integer, primary_key=True), + Column('info_pk', Integer, ForeignKey(info_table.c.pk)), + Column('start', Integer), + Column('finish', Integer)) + + + info_table.create() + rel_table.create() + data_table.create() + info_table.insert().execute( + {'pk':1, 'info':'pk_1_info'}, + {'pk':2, 'info':'pk_2_info'}, + {'pk':3, 'info':'pk_3_info'}, + {'pk':4, 'info':'pk_4_info'}, + {'pk':5, 'info':'pk_5_info'}) + + rel_table.insert().execute( + {'rel_pk':1, 'info_pk':1, 'start':10, 'finish':19}, + {'rel_pk':2, 'info_pk':1, 'start':100, 'finish':199}, + {'rel_pk':3, 'info_pk':2, 'start':20, 'finish':29}, + {'rel_pk':4, 'info_pk':3, 'start':13, 'finish':23}, + {'rel_pk':5, 'info_pk':5, 'start':15, 'finish':25}) + + data_table.insert().execute( + {'data_pk':1, 'info_pk':1, 'timeval':11, 'data_val':'11_data'}, + {'data_pk':2, 'info_pk':1, 'timeval':9, 'data_val':'9_data'}, + {'data_pk':3, 'info_pk':1, 'timeval':13, 'data_val':'13_data'}, + {'data_pk':4, 'info_pk':2, 'timeval':23, 'data_val':'23_data'}, + {'data_pk':5, 'info_pk':2, 'timeval':13, 'data_val':'13_data'}, + {'data_pk':6, 'info_pk':1, 'timeval':15, 'data_val':'15_data'}) + + + def tearDownAll(self): + data_table.drop() + rel_table.drop() + info_table.drop() + + def testone(self): + """tests a lazy load which has multiple join conditions, including two that are against + the same column in the child table""" + class Information(object): + pass + + class Relation(object): + pass + + class Data(object): + pass + + # Create the basic mappers, with no frills or modifications + Information.mapper = mapper(Information, info_table) + Data.mapper = mapper(Data, data_table) + Relation.mapper = mapper(Relation, rel_table) + + Relation.mapper.add_property('datas', relation(Data.mapper, + primaryjoin=and_(Relation.c.info_pk==Data.c.info_pk, + Data.c.timeval >= Relation.c.start, + Data.c.timeval <= Relation.c.finish), + foreignkey=Data.c.info_pk)) + + Information.mapper.add_property('rels', relation(Relation.mapper)) + + info = Information.mapper.get(1) + assert info + assert len(info.rels) == 2 + assert len(info.rels[0].datas) == 3 + +if __name__ == "__main__": + testbase.main() + + </ins></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-24 01:17:27
|
<!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>[1331] zblog: moving zblog demo to sa tree</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1331</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-23 20:17:08 -0500 (Sun, 23 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>moving zblog demo to sa tree</pre> <h3>Added Paths</h3> <ul> <li>zblog/trunk/</li> <li><a href="#zblogtrunkREADME">zblog/trunk/README</a></li> <li>zblog/trunk/bin/</li> <li><a href="#zblogtrunkbinserverpy">zblog/trunk/bin/server.py</a></li> <li>zblog/trunk/components/</li> <li><a href="#zblogtrunkcomponentscomponentsmyc">zblog/trunk/components/components.myc</a></li> <li><a href="#zblogtrunkcomponentsdatamyc">zblog/trunk/components/data.myc</a></li> <li><a href="#zblogtrunkcomponentsformmyc">zblog/trunk/components/form.myc</a></li> <li><a href="#zblogtrunkcomponentstoolbarmyc">zblog/trunk/components/toolbar.myc</a></li> <li>zblog/trunk/config/</li> <li><a href="#zblogtrunkconfigserver_configpy">zblog/trunk/config/server_config.py</a></li> <li>zblog/trunk/data/</li> <li>zblog/trunk/htdocs/</li> <li>zblog/trunk/htdocs/admin/</li> <li><a href="#zblogtrunkhtdocsadminblogmyt">zblog/trunk/htdocs/admin/blog.myt</a></li> <li><a href="#zblogtrunkhtdocsadminindexmyt">zblog/trunk/htdocs/admin/index.myt</a></li> <li><a href="#zblogtrunkhtdocsadminusermyt">zblog/trunk/htdocs/admin/user.myt</a></li> <li>zblog/trunk/htdocs/ajax/</li> <li><a href="#zblogtrunkhtdocsajaxmyghtyjaxjs">zblog/trunk/htdocs/ajax/myghtyjax.js</a></li> <li><a href="#zblogtrunkhtdocsajaxmyghtyjaxmyt">zblog/trunk/htdocs/ajax/myghtyjax.myt</a></li> <li><a href="#zblogtrunkhtdocsautohandler">zblog/trunk/htdocs/autohandler</a></li> <li>zblog/trunk/htdocs/blog/</li> <li><a href="#zblogtrunkhtdocsblogformsmyt">zblog/trunk/htdocs/blog/forms.myt</a></li> <li><a href="#zblogtrunkhtdocsblogindexmyt">zblog/trunk/htdocs/blog/index.myt</a></li> <li><a href="#zblogtrunkhtdocsblogpostmyt">zblog/trunk/htdocs/blog/post.myt</a></li> <li><a href="#zblogtrunkhtdocsblogpostcommentmyt">zblog/trunk/htdocs/blog/postcomment.myt</a></li> <li><a href="#zblogtrunkhtdocsblogviewsmyt">zblog/trunk/htdocs/blog/views.myt</a></li> <li>zblog/trunk/htdocs/bootstrap/</li> <li><a href="#zblogtrunkhtdocsbootstrapcompletemyt">zblog/trunk/htdocs/bootstrap/complete.myt</a></li> <li><a href="#zblogtrunkhtdocsbootstrapindexmyt">zblog/trunk/htdocs/bootstrap/index.myt</a></li> <li><a href="#zblogtrunkhtdocsfaviconico">zblog/trunk/htdocs/favicon.ico</a></li> <li><a href="#zblogtrunkhtdocsglobaljs">zblog/trunk/htdocs/global.js</a></li> <li><a href="#zblogtrunkhtdocsindexmyt">zblog/trunk/htdocs/index.myt</a></li> <li><a href="#zblogtrunkhtdocsloginmyt">zblog/trunk/htdocs/login.myt</a></li> <li><a href="#zblogtrunkhtdocsregistermyt">zblog/trunk/htdocs/register.myt</a></li> <li><a href="#zblogtrunkhtdocsstylecss">zblog/trunk/htdocs/style.css</a></li> <li>zblog/trunk/lib/</li> <li>zblog/trunk/lib/zblog/</li> <li><a href="#zblogtrunklibzblog__init__py">zblog/trunk/lib/zblog/__init__.py</a></li> <li>zblog/trunk/lib/zblog/controller/</li> <li><a href="#zblogtrunklibzblogcontroller__init__py">zblog/trunk/lib/zblog/controller/__init__.py</a></li> <li>zblog/trunk/lib/zblog/controller/blog/</li> <li><a href="#zblogtrunklibzblogcontrollerblog__init__py">zblog/trunk/lib/zblog/controller/blog/__init__.py</a></li> <li><a href="#zblogtrunklibzblogcontrollerblogcommentspy">zblog/trunk/lib/zblog/controller/blog/comments.py</a></li> <li><a href="#zblogtrunklibzblogcontrollerblogindexpy">zblog/trunk/lib/zblog/controller/blog/index.py</a></li> <li><a href="#zblogtrunklibzblogcontrollerbootstrappy">zblog/trunk/lib/zblog/controller/bootstrap.py</a></li> <li><a href="#zblogtrunklibzblogcontrollerfrontpy">zblog/trunk/lib/zblog/controller/front.py</a></li> <li><a href="#zblogtrunklibzblogcontrollerindexpy">zblog/trunk/lib/zblog/controller/index.py</a></li> <li><a href="#zblogtrunklibzblogcontrollerloginpy">zblog/trunk/lib/zblog/controller/login.py</a></li> <li>zblog/trunk/lib/zblog/controller/manage/</li> <li><a href="#zblogtrunklibzblogcontrollermanage__init__py">zblog/trunk/lib/zblog/controller/manage/__init__.py</a></li> <li><a href="#zblogtrunklibzblogcontrollermanageblogpy">zblog/trunk/lib/zblog/controller/manage/blog.py</a></li> <li><a href="#zblogtrunklibzblogcontrollermanageindexpy">zblog/trunk/lib/zblog/controller/manage/index.py</a></li> <li><a href="#zblogtrunklibzblogcontrollermanageuserpy">zblog/trunk/lib/zblog/controller/manage/user.py</a></li> <li>zblog/trunk/lib/zblog/database/</li> <li><a href="#zblogtrunklibzblogdatabase__init__py">zblog/trunk/lib/zblog/database/__init__.py</a></li> <li><a href="#zblogtrunklibzblogdatabasemapperspy">zblog/trunk/lib/zblog/database/mappers.py</a></li> <li><a href="#zblogtrunklibzblogdatabasetablespy">zblog/trunk/lib/zblog/database/tables.py</a></li> <li>zblog/trunk/lib/zblog/domain/</li> <li><a href="#zblogtrunklibzblogdomain__init__py">zblog/trunk/lib/zblog/domain/__init__.py</a></li> <li><a href="#zblogtrunklibzblogdomainactionspy">zblog/trunk/lib/zblog/domain/actions.py</a></li> <li><a href="#zblogtrunklibzblogdomainblogpy">zblog/trunk/lib/zblog/domain/blog.py</a></li> <li><a href="#zblogtrunklibzblogdomainuserpy">zblog/trunk/lib/zblog/domain/user.py</a></li> <li>zblog/trunk/lib/zblog/util/</li> <li><a href="#zblogtrunklibzblogutil__init__py">zblog/trunk/lib/zblog/util/__init__.py</a></li> <li><a href="#zblogtrunklibzblogutilformpy">zblog/trunk/lib/zblog/util/form.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="zblogtrunkREADME"></a> <div class="addfile"><h4>Added: zblog/trunk/README (1330 => 1331)</h4> <pre class="diff"><span> <span class="info">--- zblog/trunk/README 2006-04-24 01:15:49 UTC (rev 1330) +++ zblog/trunk/README 2006-04-24 01:17:08 UTC (rev 1331) </span><span class="lines">@@ -0,0 +1,129 @@ </span><ins>+Zblog Demo Application + +Yes, its a blog. Probably not a blog app you'd want to use anywhere, although +it could be taken as a starting point to building a more featured application. +Its pretty much a plain white interface that lets you create users, blogs, +blog posts, and comments, with everything reading fresh from the database, +i.e. no file generation or caching or anything. + +The point of it is to illustrate a fairly powerful application layout, which +includes Ajax functionality, basic SQLAlchemy integration, a rudimentary +security framework, and form-handling paradigms that work across regular and +Ajax-style layouts. + +ARCHITECTURE +------------ +The blog application is basically providing a management interface to the +following graph of objects: + + Users + | + +----Blogs <-TopicAssociations-> Topics + | + +----Posts + | + +---Comments<--+ + + | + | | + +-------+ + +The application is laid out something like this: + +Server Application Persistence +------ --------------------- ------------ +bin/server.py ---> lib/zblog/__init__.py --> lib/zblog/database/tables.py +config/server_config.py config/config.py lib/zblog/database/mappers.py + + | + v +View Controller Domain +----- ----------- -------- +htdocs/*.myt <-- lib/zblog/controller/__init__.py <-- lib/zblog/domain/user.py +components/*.myc lib/zblog/controller/front.py lib/zblog/domain/blog.py + lib/zblog/controller/*.py lib/zblog/domain/actions.py + lib/zblog/util/form.py + +Not included above are a few modules in the "bootstrap" category, which are +involved with auto-configuring the application and creating its database +tables when it is run for the first time. + +Server +------ +The server modules deal with running the application. bin/server.py is a basic +Myghty HTTPServerHandler runner, which reads the interpreter configuration +from config/server_config.py. Other runners, such as WSGI, Paste, mod_python, +etc. can be used instead with server_config.py. + +Application +----------- +The base application includes the zblog __init__.py module, which provides +some initialization functions and the ability for other packages to add +themselves to the overall startup process, and the config.py configuration +which includes application-wide config. This file is generated by the initial +"bootstrap" runner. + +Persistence +----------- +tables.py defines the SQLAlchemy table metadata for all tables, which is also +used to create the tables, and mappers.py defines the datamapping +relationships from the tables to the domain objects. + +Domain +------ +this is the center of the app, user.py includes a User object as well as some +basic crypt() functions for passwords, and blog.py includes all the rest of +the objects represented. actions.py defines a set of Action objects which +correspond to specific user actions and states. A single method access() +provides a yes/no response indicating if a particular action is allowed. + +Controller +---------- +The base controller class is in __init__.py, and an application-wide "front" +controller is in front.py. All HTTP requests, including Ajax calls, go through +the front controller. The front controller then locates the actual controller +to be called, examines its security attributes which may also include an +actions.Action object, and either passes along or raises a 403 return code. + +The security attributes are set via a decorator, and this decorator is the +primary method of marking a particular controller method as publically +accessible. The front controller uses its own "resolver context" in order to +locate the secondary controller object; this context is configured in +config/server_config.py. + +Each controller then handles its appropriate section, and makes heavy use of +the Form objects defined in util/form.py in order to send information about +form status to views. + +View +---- +All the Myghty templates, HTML, JS, CSS, including the Myghtyjax files. Form +handling is largely assisted by the components/components.myc package which +provides formfield rendering methods that integrate with the util/form.py +objects. + +RUNNING THE DEMO +---------------- +1. We're using decorators, so you should use Python 2.4 or greater. +2. insure that SQLAlchemy is installed (http://www.sqlalchemy.org/). +3. insure that this latest source tree of Myghty is either installed or in the +PYTHONPATH. +4. insure that either pysqlite, psygopg/Postgres, mysqldb/mysql is installed (or +Oracle/cx_Oracle, but not tested as much). +5. run the server: python ./bin/server.py +6. Go to the URL: http://localhost:8080/ +7. A "bootstrap" page should appear. enter an admin username, password, and +set up database parameters (foolproof defaults are provided for SQLite), then +press "connect" to try connecting to the database. +8. when connect is successful, press "create config file". this should bounce +to a textual screen of table create statements. it also writes a config file +in ./config/config.py . +9. to login, press "login" and login as the administrator user. +10. create a few test users, and a few "blogs". Then maybe a few posts inside +the blogs. +11. the console where the server.py is running should be dumping all the HTTP +requests as well as all the SQL calls and their results. The logging of SQL +calls can be switched off in the config/config.py file. +12. watch what happens when you create a user, then create some blogs for that +user. then delete the user. All the blogs, posts, comments owned by that user +(and comment replies) get deleted too ! amazing. the mapping relationships are +completely defined within lib/zblog/database/mappers.py. </ins></span></pre></div> <a id="zblogtrunkbinserverpy"></a> <div class="addfile"><h4>Added: zblog/trunk/bin/server.py (1330 => 1331)</h4> <pre class="diff"><span> <span class="info">--- zblog/trunk/bin/server.py 2006-04-24 01:15:49 UTC (rev 1330) +++ zblog/trunk/bin/server.py 2006-04-24 01:17:08 UTC (rev 1331) </span><span class="lines">@@ -0,0 +1,32 @@ </span><ins>+#!/usr/local/bin/python + +"""myghty HTTPServerHandler runner.""" + +import myghty.http.HTTPServerHandler as HTTPServerHandler +import sys, os, re +from myghty.resolver import * + +root = os.path.abspath(os.path.join(os.path.dirname(__file__), '../')) +sys.path.insert(0, os.path.join(root, 'lib')) + +interpreter_config = { + 'root' : root +} +execfile(os.path.join(root, 'config/server_config.py'), globals(), interpreter_config) +interp = HTTPServerHandler.HSHandler(**interpreter_config) + +port = 8080 +httpd = HTTPServerHandler.HTTPServer( + port = port, + + handlers = [ + {r'.*(?:/|\.myt)$' : interp}, + ], + + docroot = [{'.*' : os.path.join(root, 'htdocs')}], + +) + +print "Listening on port %d" % port +httpd.serve_forever() + </ins></span></pre></div> <a id="zblogtrunkcomponentscomponentsmyc"></a> <div class="addfile"><h4>Added: zblog/trunk/components/components.myc (1330 => 1331)</h4> <pre class="diff"><span> <span class="info">--- zblog/trunk/components/components.myc 2006-04-24 01:15:49 UTC (rev 1330) +++ zblog/trunk/components/components.myc 2006-04-24 01:17:08 UTC (rev 1331) </span><span class="lines">@@ -0,0 +1,228 @@ </span><ins>+<%global> + import string, re +</%global> + +<%method user> + <%init> + return m.get_session().get('user', None) + </%init> +</%method> + +<%method securehref> + <%args> + href + action + </%args> + <%init> + user = m.get_session().get('user', None) + </%init> +% if action.access(user, **ARGS): + <a href="<% href %>"><% m.content() %></a> +% else: + <span class="grey"><% m.content() %></span> +% + +</%method> + +<%method entryform> + <%args> + name=None + action=None + onsubmit=None + ajaxtarget=None + form=None + </%args> + <%init> + if form is not None: + name = form.name + m.attributes[(self.owner.id, 'form')] = form + if ajaxtarget is not None: + onsubmit = m.comp("SELF:ajaxaction", target=ajaxtarget, form=form, name=name) + ";return false;" + </%init> + <form method="post" name="<% name %>" action="<% action %>" onsubmit="<% onsubmit %>"> + <&|SELF:fields&> + <% m.content() %> + </&> + </form> + <%cleanup> + try: + del m.attributes[(self.owner.id, 'form')] + except KeyError: + pass + </%cleanup> +</%method> + +<%method ajaxaction> + <%args> + target + form=None + name=None + </%args> + <%init> + if form is not None: + if name is None: + name = form.name + formargs = m.scomp('SELF:formargs', form=form) + else: + formargs = '' + return target + "(%s)" % formargs + </%init> +</%method> + +<%method formargs trim="both"> + <%args> + form + name=None + </%args> +getargs(document.<% name or form.name %>, <% string.join(["'%s'" % f.displayname for f in form], ',') %>) +</%method> + + +<%method fields> + <table> + <% m.content() %> + </table> +</%method> + +<%method field> + <%args> + type + name=None + field=None + </%args> + <%init> + label = m.content() + if field is None: + form = m.attributes.get((self.owner.id, 'form'), None) + if form is not None and name: + field = form.get(name, None) + + if field is not None: + ARGS['name'] = field.displayname + ARGS.setdefault('value', field.display) + ARGS['data'] = field.data + ARGS['field'] = field + else: + if name is not None: + ARGS.setdefault('value', m.root_request_args.get(name, None)) + component = m.fetch_component("SELF:%s" % type) + </%init> +% if component.attributes.get('hidden', False) or not label: + <% m.comp(component, **ARGS) %> +% else: + <tr> + <td valign="top"><% label %></td> + <td><%python>m.comp(component, **ARGS)</%python> +% if field is not None: + <& /form:fieldstatus, field=field &> +% + </td> + </tr> +% +</%method> + +<%method row> + <%args> + colspan=None + </%args> + <tr> + <td <% colspan and 'colspan="%s"' % colspan or '' %>> + <% m.content() %> + </td> + </tr> +</%method> + + +<%method text> +<%args> + size=20 + name + value +</%args> +<input type="text" size="<% size %>" name="<% name %>" value="<% value %>"> +</%method> + +<%method password> +<%args> + size=20 + name + value +</%args> +<input type="password" size="<% size %>" name="<% name %>" value="<% value %>"> +</%method> + +<%method textarea> +<%args> + rows=5 + cols=50 + name + value +</%args> +<textarea rows="<% rows %>" cols="<% cols %>" name="<% name %>"><% value %></textarea> +</%method> + +<%method hidden> +<%args> + name=None + value +</%args> +<%attr> + hidden=True +</%attr> +<input type="hidden" name="<% name %>" value="<% value %>"> +</%method> + +<%method dropdown> +<%args> + name + data=() + onselect=None + value=None +</%args> +<select name="<% name %>" <% onselect and 'onChange="%s(this.value)"' % onselect or "" %>> +% for entry in data: +<option value="<% entry[0] %>" <% value == entry[0] and 'selected' %> ><% entry[1] %></option> +% +% if m.has_content(): +<% m.content() %> +% +</select> +</%method> + +<%method submit> + <%args> + value + </%args> + <input type="submit" value="<% value %>"> +</%method> + +<%method button> + <%args> + value + onclick + </%args> + <input type="button" value="<% value %>" onClick=<% repr(str(onclick)) %>> +</%method> + +<%method popup> + <%args> + name + </%args> + <!-- popup <% name %>--> + <tr><td colspan="2"><div id="<% name %>"><% m.content() %></div></td></tr> + <!-- end popup --> +</%method> + +<%method confirm> +<%args> + yes + no +</%args> +<div class="confirm"> +<% m.content() %><br/> +<center> + <&/components:field, type="button", value="Yes", onclick=yes &> + <&/components:field, type="button", value="No", onclick=no &> +</center> +</div> +</%method> </ins><span class="cx">\ No newline at end of file </span></span></pre></div> <a id="zblogtrunkcomponentsdatamyc"></a> <div class="addfile"><h4>Added: zblog/trunk/components/data.myc (1330 => 1331)</h4> <pre class="diff"><span> <span class="info">--- zblog/trunk/components/data.myc 2006-04-24 01:15:49 UTC (rev 1330) +++ zblog/trunk/components/data.myc 2006-04-24 01:17:08 UTC (rev 1331) </span><span class="lines">@@ -0,0 +1,76 @@ </span><ins>+<%doc> + defines a set of "data listing" methods, which provide a template-oriented + interface to various kinds of list-based information. +</%doc> + +<%global> + from zblog.domain.blog import * +</%global> + +<%method bloglist> +<%args> + loop=True + user=None +</%args> +<%init> + if user is not None: + blogs = Blog.mapper.select_by(owner_id=user.id) + else: + blogs = Blog.mapper.select() + if not m.has_content(): + return blogs +</%init> +% if loop: +% for b in blogs: + <% m.content(blog=b) %> +% +% else: + <% m.content(blogs=blogs) %> +% +</%method> + +<%method blogurl trim="both"> + <%args> + blog + </%args> +/blog/<% repr(blog.id) %>/ +</%method> + +<%method blogposts> +<%args> + loop=True + blog + keyword=False +</%args> +<%init> + posts = Post.mapper.select_by(blog_id=blog.id, keyword=keyword) + if not m.has_content(): + return posts +</%init> +% if loop: +% for p in posts: + <% m.content(post=p) %> +% +% else: + <% m.content(posts=posts) %> +% +</%method> + +<%method postcomments> + <%args> + loop=True + post + </%args> + <%init> + comments = Comment.find_by_post(post) + if not m.has_content(): + return comments + </%init> +% if loop: +% for c in comments: + <% m.content(comment=c) %> +% +% else: + <% m.content(comments=comments) %> +% +</%method> </ins></span></pre></div> <a id="zblogtrunkcomponentsformmyc"></a> <div class="addfile"><h4>Added: zblog/trunk/components/form.myc (1330 => 1331)</h4> <pre class="diff"><span> <span class="info">--- zblog/trunk/components/form.myc 2006-04-24 01:15:49 UTC (rev 1330) +++ zblog/trunk/components/form.myc 2006-04-24 01:17:08 UTC (rev 1331) </span><span class="lines">@@ -0,0 +1,45 @@ </span><ins>+<%doc> + method: fieldstatus + purpose: displays a blue or red asterisk, given a form.FormField object +</%doc> +<%method fieldstatus trim="both"> + <%args> + field = None + </%args> + <%init>if field is None:return</%init> +% if field.is_valid() is False: + <span style="color:red">*</span> +% elif field.required: + <span style="color:blue">*</span> +% +</%method> + + +<%doc> + method: formstatus + purpose: displays a list of invalid fields, given a form.Form object +</%doc> +<%method formstatus> + <%args> + form + </%args> + <%init>if form is None: return</%init> +<div> +% for success in form.success: + <span style="color:green"><% success %></span><br/> +% +% if form.is_valid(): +% return +% +% for error in form.errors: + <span style="color:red"><% error %></span><br/> +% +% for element in form: +% if element.is_valid() is False: +% for error in element.errors: + <span style="color:red"><% error %></span><br/> +% +% +% +</div> +</%method> </ins><span class="cx">\ No newline at end of file </span></span></pre></div> <a id="zblogtrunkcomponentstoolbarmyc"></a> <div class="addfile"><h4>Added: zblog/trunk/components/toolbar.myc (1330 => 1331)</h4> <pre class="diff"><span> <span class="info">--- zblog/trunk/components/toolbar.myc 2006-04-24 01:15:49 UTC (rev 1330) +++ zblog/trunk/components/toolbar.myc 2006-04-24 01:17:08 UTC (rev 1331) </span><span class="lines">@@ -0,0 +1,42 @@ </span><ins>+<%init> + s = m.get_session() + user = s.get('user', None) + + quotes = [ + "Welcome User #1!", + 'We Put the "P" in "Plain"', + 'The Best Completely Unfinished Blog App Out There', + 'Voted #1 - On Your Desktop', + 'Its a Great Idea - While it Lasts', + 'Over One Million Downloads - In Backwards Negative UpsideDown Land !', + "Has No Graphics - *And* Doesn't Work in Lynx !", + "Oh We've Got Monkeys. And They're Typin'....Just You Wait.", + ] + import random + quote = quotes[random.randint(0, len(quotes)-1)] +</%init> + +<div class="header"> +<span class="large">Zblog</span> - <span class="slogan">"<% quote %>"</span> + +</div> + +<div class="toolbar"> + +% if user is not None: + <div class="loginstatus"> + you are logged in as <b><% user.name %></b> + </div> +% + +<a href="/">Home</a> +<&|/components:securehref, href="/blog/my/", action=actions.LoggedIn() &>My Blogs</&> +<&|/components:securehref, href="/manage/", action=actions.Manage() &>Manage</&> + +% if user is not None: + <a href="/login/logout/">Logout</a> +% else: + <&|/components:securehref, href="/login/", action=actions.Login() &>Login</&> + <&|/components:securehref, href="/login/register/", action=actions.Register() &>Register</&> +% +</div> </ins></span></pre></div> <a id="zblogtrunkconfigserver_configpy"></a> <div class="addfile"><h4>Added: zblog/trunk/config/server_config.py (1330 => 1331)</h4> <pre class="diff"><span> <span class="info">--- zblog/trunk/config/server_config.py 2006-04-24 01:15:49 UTC (rev 1330) +++ zblog/trunk/config/server_config.py 2006-04-24 01:17:08 UTC (rev 1331) </span><span class="lines">@@ -0,0 +1,62 @@ </span><ins>+ +"""Myghty server configuration. should be neutral to HTTP environment, i.e. WSGI, standalone, etc. + +This file should be executed in a context that includes "root" as the path to the application root.""" + + +data_dir=os.path.join(root, 'cache') + +resolver_strategy = [ + # request-level resolution: everything goes to the front controller + ConditionalGroup( + context="request", rules=[ + ResolveModule({r'(.*)' : 'zblog.controller.front:index'}), + NotFound(), + ] + ), + + # front-controller resolution: front controller forwards requests into this realm + # after performing security checks + ConditionalGroup( + context="frontcontroller", rules=[ + ResolvePathModule(os.path.join(root, 'lib/zblog/controller'), path_stringtokens=['index'], path_moduletokens=['index']), + NotFound() + ] + ), + + # component/template resolution, for subrequests, component calls, inheritance + + # autohandlers + ResolveUpwards(), + + # for the 'components' directory, we append '.myc' to incoming filenames so templates dont have to + # specify + ResolveFile({'components':os.path.join(root, 'components')}, adjust=lambda u: re.sub(r'$', '.myc', u)), + + # page-level resolution; we convert directory names to index.myt + PathTranslate((r'/$', '/index.myt')), + ResolveFile({'htdocs':os.path.join(root, 'htdocs')}), +] + +#debug_elements=['resolution'] + +attributes = { + 'config_file' : os.path.join(root, 'config/config.py'), +} + +def preproc(source): + """source pre-processor. adds an import to the top of all components.""" + return """ +<%global> +import zblog.domain.actions as actions +</%global> +""" + source +python_pre_processor = preproc + + + +# startup stuff. +import zblog +import zblog.database +zblog.load_config(attributes['config_file']) + </ins></span></pre></div> <a id="zblogtrunkhtdocsadminblogmyt"></a> <div class="addfile"><h4>Added: zblog/trunk/htdocs/admin/blog.myt (1330 => 1331)</h4> <pre class="diff"><span> <span class="info">--- zblog/trunk/htdocs/admin/blog.myt 2006-04-24 01:15:49 UTC (rev 1330) +++ zblog/trunk/htdocs/admin/blog.myt 2006-04-24 01:17:08 UTC (rev 1331) </span><span class="lines">@@ -0,0 +1,85 @@ </span><ins>+ +<%method ajax> +<%init> + return m.comp('/ajax/myghtyjax.myt:init', + editblog = { + 'handler_uri':'/manage/blog/ajax_editblog/', + 'exectype':'writedom', + 'dom_id':'opwindow' + }, + bloglist = { + 'handler_uri':'/manage/blog/bloglist/', + 'exectype':'writedom', + 'dom_id':'opwindow' + }, + action_editblog = { + 'handler_uri':'/manage/blog/edit_blog/', + 'exectype':'writedom', + 'dom_id':'opwindow' + }, + action_deleteblog = { + 'handler_uri':'/manage/blog/delete_blog/', + 'exectype':'writedom', + 'dom_id':'opwindow' + }, + ) +</%init> +</%method> + +<%method links> +% for link in [('javascript:editblog()', 'Create Blog'), ('javascript:bloglist()', 'Edit Blog')]: +<% m.content(url=link[0], text=link[1]) %> +% +</%method> + +<%method blogedit> + <%args> + form + </%args> + +<& /form:formstatus, form=form &> +<&|/components:entryform, ajaxtarget="action_editblog", form=form, columns=2&> + <&/components:field, type="hidden", name='blog_id'&> + <&|/components:field, type="text", name='name'&> + Blog Name + </&> + <&|/components:field, type="text", name='description'&> + Description + </&> + <&|/components:field, type="text", name='owner_name'&> + Owner + </&> + <&|/components:row, colspan="2"&> +% if form['blog_id'].display: + <&/components:field, type="submit", value="Update Blog"&> + <&/components:field, type="button", value="Delete Blog", onclick=m.comp('/components:ajaxaction', target="action_deleteblog", form=form)&> +% else: + <&/components:field, type="submit", value="Create Blog" &> +% + </&> +</&> +</%method> + +<%method bloglist> + +<&|/components:entryform, name="lookupform", onsubmit="editblog({'blog_id':document.lookupform.blog.value});return false", columns=2&> +<tr> + <td>Select a blog:</td> + <td> + <&/components:dropdown, name="blog", data=[(blog.id, blog.name) for blog in m.comp('/data:bloglist')] &> + <&/components:submit, value="Go"&> + </td> +</tr> +</&> +</%method> + +<%method delete_confirm> + <%args> + blog + </%args> + <&|/components:confirm, yes="action_deleteblog({'blog_id':'%s','confirm':1})" % blog.id, + no="editblog({'blog_id':'%s'})" % blog.id&> + Are you sure you want to delete blog '<% blog.name %>'? + This will delete all posts and comments within this blog. + </&> +</%method> </ins><span class="cx">\ No newline at end of file </span></span></pre></div> <a id="zblogtrunkhtdocsadminindexmyt"></a> <div class="addfile"><h4>Added: zblog/trunk/htdocs/admin/index.myt (1330 => 1331)</h4> <pre class="diff"><span> <span class="info">--- zblog/trunk/htdocs/admin/index.myt 2006-04-24 01:15:49 UTC (rev 1330) +++ zblog/trunk/htdocs/admin/index.myt 2006-04-24 01:17:08 UTC (rev 1331) </span><span class="lines">@@ -0,0 +1,20 @@ </span><ins>+<%init> + if m.comp('blog.myt:ajax') or m.comp('user.myt:ajax'): return +</%init> + +<& /ajax/myghtyjax.myt:js &> + +<h3>ZBlog Management</h3> + +<ul> + <&|blog.myt:links&> + <li><a href="<% m.content_args['url'] %>"><% m.content_args['text'] %></a></li> + </&> + <&|user.myt:links&> + <li><a href="<% m.content_args['url'] %>"><% m.content_args['text'] %></a></li> + </&> +</ul> + +<div id="opwindow"> +</div> + </ins></span></pre></div> <a id="zblogtrunkhtdocsadminusermyt"></a> <div class="addfile"><h4>Added: zblog/trunk/htdocs/admin/user.myt (1330 => 1331)</h4> <pre class="diff"><span> <span class="info">--- zblog/trunk/htdocs/admin/user.myt 2006-04-24 01:15:49 UTC (rev 1330) +++ zblog/trunk/htdocs/admin/user.myt 2006-04-24 01:17:08 UTC (rev 1331) </span><span class="lines">@@ -0,0 +1,87 @@ </span><ins>+<%method ajax> +<%init> + return m.comp('/ajax/myghtyjax.myt:init', + useradmin={ + 'handler_uri':'/manage/user/ajax_edituser/', + 'exectype':'writedom', + 'dom_id':'opwindow' + }, + action_edituser={ + 'handler_uri':'/manage/user/edit_user/', + 'exectype':'writedom', + 'dom_id':'opwindow' + }, + action_deluser={ + 'handler_uri':'/manage/user/delete_user/', + 'exectype':'writedom', + 'dom_id':'opwindow' + } + ) +</%init> +</%method> + + +<%method links> +% for link in [('javascript:useradmin()', 'User Administration')]: +<% m.content(url=link[0], text=link[1]) %> +% +</%method> + +<%method userform> + <%args> + form + </%args> + +<& /form:formstatus, form=form &> + +<&|/components:entryform, name="lookupform", onsubmit="useradmin({'username':document.lookupform.username.value});return false;", columns=2&> + <tr> + <td> + Lookup User: + </td> + <td> + <&/components:field, type="text", name="username"&> + <&/components:field, type="submit", value="Find"&> + </td> + </tr> +</&> + +<&|/components:entryform, ajaxtarget="action_edituser", form=form, columns=2&> + <&/components:field, type="hidden", name='user_id'&> + <&|/components:field, type="text", name='name' &> + User Name + </&> + <&|/components:field, type="text", name='fullname'&> + Full Name + </&> + <&|/components:field, type="dropdown", name='group'&> + Group + </&> + <&|/components:field, type="password", name='password_set'&> + Password + </&> + <&|/components:field, type="password", name='password_repeat'&> + Repeat Password + </&> + <&|/components:row, colspan="2" &> +% if form['user_id'].value: + <&/components:field, type="submit", value="Update User"&> + <&/components:field, type="button", value="Delete User", onclick=m.comp('/components:ajaxaction', target="action_deluser", form=form) &> +% else: + <&/components:field, type="submit", value="Create User"&> +% + </&> + +</&> +</%method> + +<%method delete_confirm> + <%args> + user + </%args> + <&|/components:confirm, yes="action_deluser({'user_id':'%s','confirm':1})" % user.id, + no="useradmin({'username':'%s'})" % user.name&> + Are you sure you want to delete user '<% user.fullname %>'? + This also deletes all blogs and posts by that user. + </&> +</%method> </ins><span class="cx">\ No newline at end of file </span></span></pre></div> <a id="zblogtrunkhtdocsajaxmyghtyjaxjs"></a> <div class="addfile"><h4>Added: zblog/trunk/htdocs/ajax/myghtyjax.js (1330 => 1331)</h4> <pre class="diff"><span> <span class="info">--- zblog/trunk/htdocs/ajax/myghtyjax.js 2006-04-24 01:15:49 UTC (rev 1330) +++ zblog/trunk/htdocs/ajax/myghtyjax.js 2006-04-24 01:17:08 UTC (rev 1331) </span><span class="lines">@@ -0,0 +1,106 @@ </span><ins>+ + function error(message) { + var console = window.open("",'console',"width="+400+",height="+400+",status=yes,dependent=yes,resizable=yes,scrollbars=yes"); + console.document.write(message); + console.document.close(); + } + + + /* + receives javascript from the given url + args and evaluates it + */ + function runRemoteJS(url, args) { + doCall(url, function (x) {eval(x);}, args); + } + + function doCall(url, wrapper, args) { + var req = openConnection(); + if (url.indexOf('?') == -1) { + url = url + "?"; + } + for (var key in args) { + if (key=='_mjax_named') { + for (var k in args[key]) { + url = url + '&' + escape(k) + "=" + escape(args[key][k] || ''); + } + } + else { + url = url + '&' + escape(key) + "=" + escape(args[key] || ''); + } + } + + url = url + "&_rnd=" + new Date().getTime() + + req.open("GET", url); + + req.onreadystatechange = function () { + if (req.readyState != 4) { + return; + } + + //var s = getResponse(); + var s = req.responseText; + if (s) { + try { + wrapper(s); + } + catch (e) { + error(e + "<br/><br/>\n\nArgument:\n<br/>" + s); + } + } + + } + req.send(null); + delete req; + + } + + + function openConnection () { + var conn = null; + try { + conn = new ActiveXObject("Msxml2.XMLHTTP"); + } + catch (e) { + try { + conn = new ActiveXObject("Microsoft.XMLHTTP"); + } + catch (oc) { } + } + if(!conn && typeof XMLHttpRequest != "undefined") { + conn = new XMLHttpRequest(); + } + + if (!conn) { + error("XMLHttp connection object not defined on this browser"); + } + return conn; + } + + + + /* + given the id of a document element, a url, and argument hash, populates the given document + element with the results of the url + args + */ + function populateDOM(id, url, args) { + doCall(url, function (x) { + var elem = document.getElementById(id); + if (elem) { + elem.innerHTML = x; + } + else { + error("No such element '" + id + "'"); + } + }, + args); + } + + function writeDOM(id, string) { + var elem = document.getElementById(id); + if (elem) { + elem.innerHTML = string; + } + } + + </ins></span></pre></div> <a id="zblogtrunkhtdocsajaxmyghtyjaxmyt"></a> <div class="addfile"><h4>Added: zblog/trunk/htdocs/ajax/myghtyjax.myt (1330 => 1331)</h4> <pre class="diff"><span> <span class="info">--- zblog/trunk/htdocs/ajax/myghtyjax.myt 2006-04-24 01:15:49 UTC (rev 1330) +++ zblog/trunk/htdocs/ajax/myghtyjax.myt 2006-04-24 01:17:08 UTC (rev 1331) </span><span class="lines">@@ -0,0 +1,321 @@ </span><ins>+<%flags> + # myghtyjax.myt needs to inherit from either None, or an autohandler + # that doesnt have any textual output. + inherit=None + trim="both" +</%flags> + +<%doc> + MyghtyJax - a Myghty AJAX Adapter + + The "init" method is called to register the names of javascript functions that will + map to Myghty components and/or python function definitions. supply + key=value arguments, where keys are the names of javascript functions + to be generated and the value is one of: a function object, the string + address of a Myghty component (path or method name), or a Myghty component object. + This method should be called in the %init section of the calling component, usually before + anything else has executed. If it returns true it means the XMLHttpRequest action + is taking effect, and the calling component should return immediately. + If false is returned, the component should continue on normally. + + Note that the "js" method below must be called within the HTML body of a page to + actually write out the javascript functions defined by the init method. + + components that are called can specify options for how they should be handled via their + <%attr> sections. the available options are: + + type='source' + take the HTML source of the component and send it to a supplied javascript + callback function. + + type='writedom' + take the HTML source of the component and send it to the DOM element + with the id given by either the <%attr> 'dom_id', or the first argument + to the javascript function. + + type='exec' + take the output of the component and evaluate it directly as javascript. + + Example: + + if m.comp('myghtyjax.myt:init', + mypage = 'SELF:mymethod' + ): return + + The above example will create a javascript function 'mypage()', which results in an Ajax + call back to the myghtyjax.myt page which will route the request to the <%method mymethod> + inside the current page. The results of the method will be processed per the "type" + attribute on the method. + + A second form of init argument allows the values normally inside <%attr> to be + specified directly to init: + + if m.comp('myghtyjax.myt:init', + mypage = { + 'component' : 'SELF:mymethod', + 'exectype' : 'writedom', + 'dom_id' : 'leftnav' + } + ): return + + The above example will create a javascript function 'mypage()', which results in an Ajax + call back to the myghtyjax.myt page which will route the request to the <%method mymethod> + inside the current page. The resulting output from the method will be written to the + 'leftnav' DOM element on the page. + + There is also a way to circumvent the usual URL of the myghtyjax controller + directly to any URL: + + if m.comp('myghtyjax.myt:init', + mypage = { + 'handler_uri' : '/my_ajax_page/', + 'exectype' : 'writedom', + 'dom_id' : 'leftnav' + } + ): return + + The above example will create a javascript function 'mypage()', which results in an Ajax + call directly to the uri '/my_ajax_page/', and the resulting HTML will be written into the + DOM element 'leftnav'. +</%doc> +<%method init> + <%init> + frame = m.execution_stack.pop() + try: + ret = init(m, **ARGS) + finally: + m.execution_stack.append(frame) + return ret + </%init> +</%method> + +<%doc> + js is called after init has been called, and delivers the javascript "stub" functions + that connect page javascript to server side calls via XMLHttpRequest. + + the scripturi and handleruri arguments are optional arguments that + reference the web-accessible URIs of the myghtyjax.js and myghtyjax.myt + files respectively. + + If these parameters are not supplied, they will be searched for in the interpreter + attributes "myghtyjax.handleruri" and "myghtyjax.scripturi". + if not there either, they will be determined based on the path of the original + calling component. +</%doc> +<%method js> + <%args> + scripturi = None + handleruri = None + </%args> + <%init> + js(m, handleruri = handleruri, scripturi = scripturi) + </%init> +</%method> + +<%args> + _mjax_def + _mjax_component +</%args> + +<%init> + m.comp(_mjax_component, **ARGS) +</%init> + + +<%once> + +import inspect, string, types, posixpath +import myghty.request as request + +# initialization method, receives javascript function names mapped +# to python function pointers, component names, or component objects. +# referenced by the "init" myghty method. +def init(m, **params): + run_func = m.request_args.get('_mjax_def', None) + + if (run_func is not None): + object = params[run_func] + callable = _get_callable(m, run_func, object) + callable.run(m) + return True + else: + defs = m.attributes.setdefault('__mjax_defs', {}) + for (jsname, object) in params.iteritems(): + defs[jsname] = _get_callable(m, jsname, object) + return False + +def _get_callable(m, name, object): + if isinstance(object, types.FunctionType): + return DefCallable(name, object) + elif isinstance(object, dict): + return ComponentCallable(m, name, **object) + else: + return ComponentCallable(m, name, object) + +def js(m, handleruri = None, scripturi = None): + try: + defs = m.attributes['__mjax_defs'] + except KeyError: + raise "myghtyjax.js() method requires components and defs to be init()ialized first" + + path = m.current_component.path + + if handleruri is None: + handleruri = m.interpreter.attributes.get('myghtyjax.handleruri', path) + + + if scripturi is None: + scripturi = m.interpreter.attributes.get('myghtyjax.scripturi', None) + if scripturi is None: + (dir, file) = posixpath.split(path) + scripturi = posixpath.join(dir, "myghtyjax.js") + + m.write("<script src=\"%s\"></script>\n" % scripturi) + m.write("<script>\n"); + for callable in defs.values(): + callable.init_callerpath(m) + if callable.exectype == 'writedom': + m.comp('SELF:write_writedom', callable = callable, handleruri = handleruri) + elif callable.exectype == 'source': + m.comp('SELF:write_docall', callable = callable, handleruri=handleruri) + else: + m.comp('SELF:write_remotejs', callable = callable, handleruri=handleruri) + + m.write("</script>\n") + +class JaxComponent: + def init_callerpath(self, m): + if getattr(self, 'callerpath',None) is None: + self.callerpath = m.caller.path + + def get_function_args(self, leading_comma = False): + args = string.join(self.argnames + ['_mjax_named'], ',') + if leading_comma and args: + return ', ' + args + else: + return args + + def get_handler_uri(self, handler_uri): + return handler_uri + + def get_remote_args(self, m): + return string.join( + [ + "'_mjax_def' : '%s'" % self.jsname, + "'_mjax_component' : '%s'" % self.callerpath, + "'_mjax_named':_mjax_named" + ] + + ["'%s' : %s" % (name, name) for name in self.argnames], + ",\n", + ) + + +class ComponentCallable(JaxComponent): + def __init__(self, m, jsname, component=None, exectype=None, argnames=None, dom_id=None, handler_uri=None): + self.jsname = jsname + self.component = component + self.handler_uri=handler_uri + if type(component) == types.StringType: + componentobj = m.fetch_component(component) + else: + componentobj = component + + if exectype is None: + if componentobj is not None: + self.exectype = componentobj.attributes.get('type', 'exec') + else: + self.exectype = 'exec' + else: + self.exectype = exectype + + if dom_id is None and componentobj is not None: + self.dom_id = componentobj.attributes.get('dom_id', None) + else: + self.dom_id = dom_id + + if argnames is None: + if componentobj is not None: + self.argnames = [arg.name for arg in componentobj.arguments] + else: + self.argnames = [] + else: + self.argnames = argnames + + def get_handler_uri(self, handler_uri): + if self.handler_uri is not None: + return self.handler_uri + else: + return handler_uri + + def run(self, m): + m.comp(self.component, **m.root_request_args) + + +class DefCallable(JaxComponent): + def __init__(self, jsname, defobj): + self.jsname = jsname + + if isinstance(defobj, types.MethodType): + defobj = defobj.im_func + self.defobj = defobj + self.argnames = inspect.getargspec(defobj)[0] + self.has_named = inspect.getargspec(defobj)[2] is not None + self.exectype = 'exec' + + def run(self, m): + if self.has_named: + m.write(self.defobj(**m.root_request_args)) + else: + args = {} + for name in self.argnames: + args[name] = m.root_request_args[name] + + m.write(self.defobj(**args)) + +</%once> + +<%method write_docall> + <%args> + callable + handleruri + </%args> + function <% callable.jsname %>(callback<% callable.get_function_args(leading_comma = True) %>) { + doCall('<% callable.get_handler_uri(handleruri) %>', callback, { +<% callable.get_remote_args(m) %> + }); + } +</%method> + +<%method write_remotejs> + <%args> + callable + handleruri + </%args> + function <% callable.jsname %>(<% callable.get_function_args() %>) { + runRemoteJS('<% callable.get_handler_uri(handleruri) %>', { +<% callable.get_remote_args(m) %> + }); + } +</%method> + +<%method write_writedom> + <%args> + callable + handleruri + </%args> +% if callable.dom_id is None: + function <% callable.jsname %>(domid <% callable.get_function_args(leading_comma = True) %>) { + populateDOM(domid, '<% callable.get_handler_uri(handleruri) %>', { +<% callable.get_remote_args(m) %> + }); + } +% else: + function <% callable.jsname %>(<% callable.get_function_args() %>) { + populateDOM('<% callable.dom_id %>', '<% callable.get_handler_uri(handleruri) %>', { +<% callable.get_remote_args(m) %> + }); + } +% +</%method> + + </ins></span></pre></div> <a id="zblogtrunkhtdocsautohandler"></a> <div class="addfile"><h4>Added: zblog/trunk/htdocs/autohandler (1330 => 1331)</h4> <pre class="diff"><span> <span class="info">--- zblog/trunk/htdocs/autohandler 2006-04-24 01:15:49 UTC (rev 1330) +++ zblog/trunk/htdocs/autohandler 2006-04-24 01:17:08 UTC (rev 1331) </span><span class="lines">@@ -0,0 +1,28 @@ </span><ins>+<html> +<head> + <title><&SELF:title&></title> + <link href="/style.css" rel="stylesheet" type="text/css"></link> +</head> +<body> +<script src="/global.js"></script> +<&/toolbar&> +<div class="main"> +<&SELF:status&> +% m.call_next() +</div> +</body> +</html> + +<%method title> + ZBlog +</%method> + + +<%method status> + <%args scope="subrequest"> + status=None + </%args> +% if status is not None: + <h3><% status %></h3> +% +</%method> </ins></span></pre></div> <a id="zblogtrunkhtdocsblogformsmyt"></a> <div class="addfile"><h4>Added: zblog/trunk/htdocs/blog/forms.myt (1330 => 1331)</h4> <pre class="diff"><span> <span class="info">--- zblog/trunk/htdocs/blog/forms.myt 2006-04-24 01:15:49 UTC (rev 1330) +++ zblog/trunk/htdocs/blog/forms.myt 2006-04-24 01:17:08 UTC (rev 1331) </span><span class="lines">@@ -0,0 +1,107 @@ </span><ins>+<%method postform> + <%args> + form + blog + post=None + preview=None + </%args> + +% if preview is not None: + <h3>Post Preview</h3> + <& views.myt:view_post, post=preview &> + <hr/> +% + +% if post is not None: + <h4>Editing '<% post.headline %>'</h4> +% else: + <h4>Posting to <% blog.name %></h4> +% + <&|/components:entryform, form=form, action="/blog/post/" &> + <&/components:field, type="hidden", name="preview", value="0"&> + <&/components:field, type="hidden", name="blog_id"&> + <&/components:field, type="hidden", name="post_id"&> + <&|/components:field, type="text", name="headline", size="50"&> + Headline + </&> + <&|/components:field, type="text", name="topic_keywords", size="50"&> + Topics<br/> + (separated by spaces) + </&> + <&|/components:field, type="textarea", name="summary", rows="3", cols="50"&> + Summary + </&> + <&|/components:field, type="textarea", name="body", rows="10", cols="50"&> + Body + </&> + <&|/components:row, colspan="2"&> +% if post is not None: + <&/components:field, type="submit", value="Update Post"&> + <&/components:field, type="button", value="Delete Post", onclick="delete_post({'post_id':'%s'})" % post.id &> +% else: + <&/components:field, type="submit", value="Post"&> +% + <&/components:field, type="button", value="Preview", onclick="document.%s.preview.value=1;document.%s.submit()" % (form.name, form.name)&> + </&> + </&> +</%method> + + +<%method status> + <%args> + form + </%args> +<& /form:formstatus, form=form &> +</%method> + +<%method delete_confirm> + <%args> + post + </%args> + <&|/components:confirm, yes="delete_post({'post_id':'%s','confirm':1})" % post.id, + no="edit_post({'post_id':'%s'})" % post.id&> + <h3>Delete post '<% post.headline %>'</h3> + Are you sure you want to delete this post ? This will also + delete all comments related to this post. + </&> +</%method> + + +<%method commentform> + <%args> + form + parent=None + post + </%args> + <%init> + user = m.comp('/components:user') + </%init> +<a name="reply"></a> +% if parent is None: + <h3>Post a comment for '<% post.headline %>'</h3> +% else: + <h3>Reply to '<% parent.subject %>'</h3> +% +% if not actions.CreateComment().access(user, **ARGS): + Please <&|/components:securehref, href="/login/", action=actions.Login() &>login</&> + to post a comment ! +% return +% + + <&|/components:entryform, form=form, action="/blog/comments/post/"&> + <&/components:field, type="hidden", name="preview", value="0"&> + <&/components:field, type="hidden", name="post_id"&> + <&/components:field, type="hidden", name="parent_comment_id"&> + <&|/components:field, type="text", name="subject", size="50"&> + Subject + </&> + <&|/components:field, type="textarea", name="body", rows="10", cols="50"&> + Body + </&> + <&|/components:row, colspan="2"&> + <&/components:field, type="submit", value="Post Comment"&> + <&/components:field, type="button", value="Preview", onclick="document.%s.preview.value=1;document.%s.submit()" % (form.name, form.name)&> + </&> + </&> +</%method> + </ins></span></pre></div> <a id="zblogtrunkhtdocsblogindexmyt"></a> <div class="addfile"><h4>Added: zblog/trunk/htdocs/blog/index.myt (1330 => 1331)</h4> <pre class="diff"><span> <span class="info">--- zblog/trunk/htdocs/blog/index.myt 2006-04-24 01:15:49 UTC (rev 1330) +++ zblog/trunk/htdocs/blog/index.myt 2006-04-24 01:17:08 UTC (rev 1331) </span><span class="lines">@@ -0,0 +1,81 @@ </span><ins>+<%args> + blog + loadcomponent = None + form = None + commentform = None + keyword=False +</%args> + +<%init> + if m.comp('/ajax/myghtyjax.myt:init', + post = { + 'handler_uri':'/blog/ajax_post/', + 'exectype':'writedom', + 'dom_id':'opwindow' + }, + edit_post = { + 'handler_uri':'/blog/ajax_edit_post/', + 'exectype':'... [truncated message content] |
From: <co...@sq...> - 2006-04-24 01:16:00
|
<!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>[1330] zblog/: zblog demo will be maintained here now....</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1330</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-23 20:15:49 -0500 (Sun, 23 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>zblog demo will be maintained here now....</pre> <h3>Added Paths</h3> <ul> <li>zblog/</li> </ul> </div> <div id="patch"> <h3>Diff</h3> </div> </body> </html> |
From: <co...@sq...> - 2006-04-24 01:11:09
|
<!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>[1329] sqlalchemy/branches/schema/test: mysql up</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1329</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-23 20:10:41 -0500 (Sun, 23 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>mysql up attempted tweak to sqlsoup</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemydatabasesmysqlpy">sqlalchemy/branches/schema/lib/sqlalchemy/databases/mysql.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemydatabasespostgrespy">sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyextsqlsouppy">sqlalchemy/branches/schema/lib/sqlalchemy/ext/sqlsoup.py</a></li> <li><a href="#sqlalchemybranchesschematesttesttypespy">sqlalchemy/branches/schema/test/testtypes.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemydatabasesmysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/databases/mysql.py (1328 => 1329)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/databases/mysql.py 2006-04-23 23:55:03 UTC (rev 1328) +++ sqlalchemy/branches/schema/lib/sqlalchemy/databases/mysql.py 2006-04-24 01:10:41 UTC (rev 1329) </span><span class="lines">@@ -56,7 +56,7 @@ </span><span class="cx"> class MSTime(sqltypes.Time): </span><span class="cx"> def get_col_spec(self): </span><span class="cx"> return "TIME" </span><del>- def convert_result_value(self, value, engine): </del><ins>+ def convert_result_value(self, value, dialect): </ins><span class="cx"> # convert from a timedelta value </span><span class="cx"> if value is not None: </span><span class="cx"> return datetime.time(value.seconds/60/60, value.seconds/60%60, value.seconds - (value.seconds/60*60)) </span><span class="lines">@@ -130,67 +130,60 @@ </span><span class="cx"> ('host',"Hostname", None), </span><span class="cx"> ]} </span><span class="cx"> </span><del>-class MySQLEngine(ansisql.ANSISQLEngine): - def __init__(self, opts, module = None, **params): </del><ins>+ +class MySQLExecutionContext(default.DefaultExecutionContext): + def post_exec(self, engine, proxy, compiled, parameters, **kwargs): + if getattr(compiled, "isinsert", False): + self._last_inserted_ids = [proxy().lastrowid] + +class MySQLDialect(ansisql.ANSIDialect): + def __init__(self, module = None, **kwargs): </ins><span class="cx"> if module is None: </span><span class="cx"> self.module = mysql </span><del>- self.opts = self._translate_connect_args(('host', 'db', 'user', 'passwd'), opts) - ansisql.ANSISQLEngine.__init__(self, **params) </del><ins>+ ansisql.ANSIDialect.__init__(self, **kwargs) </ins><span class="cx"> </span><del>- def connect_args(self): - return [[], self.opts] </del><ins>+ def create_connect_args(self, opts): + opts = self._translate_connect_args(('host', 'db', 'user', 'passwd'), opts) + return [[], opts] </ins><span class="cx"> </span><ins>+ def create_execution_context(self): + return MySQLExecutionContext(self) + </ins><span class="cx"> def type_descriptor(self, typeobj): </span><span class="cx"> return sqltypes.adapt_type(typeobj, colspecs) </span><span class="cx"> </span><del>- def last_inserted_ids(self): - return self.context.last_inserted_ids - </del><span class="cx"> def supports_sane_rowcount(self): </span><span class="cx"> return False </span><span class="cx"> </span><span class="cx"> def compiler(self, statement, bindparams, **kwargs): </span><del>- return MySQLCompiler(statement, bindparams, engine=self, **kwargs) </del><ins>+ return MySQLCompiler(self, statement, bindparams, **kwargs) </ins><span class="cx"> </span><del>- def schemagenerator(self, **params): - return MySQLSchemaGenerator(self, **params) </del><ins>+ def schemagenerator(self, *args, **kwargs): + return MySQLSchemaGenerator(*args, **kwargs) </ins><span class="cx"> </span><del>- def schemadropper(self, **params): - return MySQLSchemaDropper(self, **params) </del><ins>+ def schemadropper(self, *args, **kwargs): + return MySQLSchemaDropper(*args, **kwargs) </ins><span class="cx"> </span><span class="cx"> def get_default_schema_name(self): </span><span class="cx"> if not hasattr(self, '_default_schema_name'): </span><span class="cx"> self._default_schema_name = text("select database()", self).scalar() </span><span class="cx"> return self._default_schema_name </span><del>- - def last_inserted_ids(self): - return self.context.last_inserted_ids - - def post_exec(self, proxy, compiled, parameters, **kwargs): - if getattr(compiled, "isinsert", False): - self.context.last_inserted_ids = [proxy().lastrowid] </del><span class="cx"> </span><del>- # executemany just runs normally, since we arent using rowcount at all with mysql -# def _executemany(self, c, statement, parameters): - # """we need accurate rowcounts for updates, inserts and deletes. mysql is *also* is not nice enough - # to produce this correctly for an executemany, so we do our own executemany here.""" - # rowcount = 0 - # for param in parameters: - # c.execute(statement, param) - # rowcount += c.rowcount - # self.context.rowcount = rowcount - </del><span class="cx"> def dbapi(self): </span><span class="cx"> return self.module </span><span class="cx"> </span><del>- def reflecttable(self, table): </del><ins>+ def has_table(self, connection, table_name): + cursor = connection.execute("show table status like '" + table_name + "'") + return bool( not not cursor.rowcount ) + + def reflecttable(self, connection, table): </ins><span class="cx"> # to use information_schema: </span><span class="cx"> #ischema.reflecttable(self, table, ischema_names, use_mysql=True) </span><span class="cx"> </span><del>- tabletype, foreignkeyD = self.moretableinfo(table=table) </del><ins>+ tabletype, foreignkeyD = self.moretableinfo(connection, table=table) </ins><span class="cx"> table.kwargs['mysql_engine'] = tabletype </span><span class="cx"> </span><del>- c = self.execute("describe " + table.name, {}) </del><ins>+ c = connection.execute("describe " + table.name, {}) </ins><span class="cx"> while True: </span><span class="cx"> row = c.fetchone() </span><span class="cx"> if row is None: </span><span class="lines">@@ -220,7 +213,7 @@ </span><span class="cx"> default=default </span><span class="cx"> ))) </span><span class="cx"> </span><del>- def moretableinfo(self, table): </del><ins>+ def moretableinfo(self, connection, table): </ins><span class="cx"> """Return (tabletype, {colname:foreignkey,...}) </span><span class="cx"> execute(SHOW CREATE TABLE child) => </span><span class="cx"> CREATE TABLE `child` ( </span><span class="lines">@@ -229,7 +222,7 @@ </span><span class="cx"> KEY `par_ind` (`parent_id`), </span><span class="cx"> CONSTRAINT `child_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`) ON DELETE CASCADE\n) TYPE=InnoDB </span><span class="cx"> """ </span><del>- c = self.execute("SHOW CREATE TABLE " + table.name, {}) </del><ins>+ c = connection.execute("SHOW CREATE TABLE " + table.name, {}) </ins><span class="cx"> desc = c.fetchone()[1].strip() </span><span class="cx"> tabletype = '' </span><span class="cx"> lastparen = re.search(r'\)[^\)]*\Z', desc) </span><span class="lines">@@ -290,3 +283,5 @@ </span><span class="cx"> def visit_index(self, index): </span><span class="cx"> self.append("\nDROP INDEX " + index.name + " ON " + index.table.name) </span><span class="cx"> self.execute() </span><ins>+ +dialect = MySQLDialect </ins><span class="cx">\ No newline at end of file </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemydatabasespostgrespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py (1328 => 1329)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py 2006-04-23 23:55:03 UTC (rev 1328) +++ sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py 2006-04-24 01:10:41 UTC (rev 1329) </span><span class="lines">@@ -181,11 +181,9 @@ </span><span class="cx"> ]} </span><span class="cx"> </span><span class="cx"> class PGExecutionContext(default.DefaultExecutionContext): </span><del>- def pre_exec(self, engine, proxy, statement, parameters, **kwargs): - return </del><span class="cx"> </span><span class="cx"> def post_exec(self, engine, proxy, compiled, parameters, **kwargs): </span><del>- if getattr(compiled, "isinsert", False) and self._last_inserted_ids is None: </del><ins>+ if getattr(compiled, "isinsert", False) and self.last_inserted_ids is None: </ins><span class="cx"> if not engine.dialect.use_oids: </span><span class="cx"> pass </span><span class="cx"> # will raise invalid error when they go to get them </span><span class="lines">@@ -227,6 +225,9 @@ </span><span class="cx"> opts['port'] = str(opts['port']) </span><span class="cx"> return ([], opts) </span><span class="cx"> </span><ins>+ def create_execution_context(self): + return PGExecutionContext(self) + </ins><span class="cx"> def type_descriptor(self, typeobj): </span><span class="cx"> if self.version == 2: </span><span class="cx"> return sqltypes.adapt_type(typeobj, pg2_colspecs) </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyextsqlsouppy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/ext/sqlsoup.py (1328 => 1329)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/ext/sqlsoup.py 2006-04-23 23:55:03 UTC (rev 1328) +++ sqlalchemy/branches/schema/lib/sqlalchemy/ext/sqlsoup.py 2006-04-24 01:10:41 UTC (rev 1329) </span><span class="lines">@@ -37,9 +37,9 @@ </span><span class="cx"> args may either be an SQLEngine or a set of arguments suitable </span><span class="cx"> for passing to create_engine </span><span class="cx"> """ </span><del>- from sqlalchemy.engine import SQLEngine </del><ins>+ from sqlalchemy.sql import Engine </ins><span class="cx"> # meh, sometimes having method overloading instead of kwargs would be easier </span><del>- if isinstance(args[0], SQLEngine): </del><ins>+ if isinstance(args[0], Engine): </ins><span class="cx"> engine = args.pop(0) </span><span class="cx"> if args or kwargs: </span><span class="cx"> raise ArgumentError('Extra arguments not allowed when engine is given') </span></span></pre></div> <a id="sqlalchemybranchesschematesttesttypespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/testtypes.py (1328 => 1329)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/testtypes.py 2006-04-23 23:55:03 UTC (rev 1328) +++ sqlalchemy/branches/schema/test/testtypes.py 2006-04-24 01:10:41 UTC (rev 1329) </span><span class="lines">@@ -89,7 +89,7 @@ </span><span class="cx"> ) </span><span class="cx"> </span><span class="cx"> for aCol in testTable.c: </span><del>- self.assertEquals(expectedResults[aCol.name], db.schemagenerator().get_column_specification(aCol)) </del><ins>+ self.assertEquals(expectedResults[aCol.name], db.dialect.schemagenerator(db, None).get_column_specification(aCol)) </ins><span class="cx"> </span><span class="cx"> class UnicodeTest(AssertMixin): </span><span class="cx"> """tests the Unicode type. also tests the TypeDecorator with instances in the types package.""" </span><span class="lines">@@ -113,15 +113,15 @@ </span><span class="cx"> self.assert_(isinstance(x['unicode_data'], unicode) and x['unicode_data'] == unicodedata) </span><span class="cx"> if isinstance(x['plain_data'], unicode): </span><span class="cx"> # SQLLite returns even non-unicode data as unicode </span><del>- self.assert_(sys.modules[db.engine.__module__].descriptor()['name'] == 'sqlite') </del><ins>+ self.assert_(db.name == 'sqlite') </ins><span class="cx"> self.echo("its sqlite !") </span><span class="cx"> else: </span><span class="cx"> self.assert_(not isinstance(x['plain_data'], unicode) and x['plain_data'] == rawdata) </span><span class="cx"> def testengineparam(self): </span><span class="cx"> """tests engine-wide unicode conversion""" </span><del>- prev_unicode = db.engine.convert_unicode </del><ins>+ prev_unicode = db.engine.dialect.convert_unicode </ins><span class="cx"> try: </span><del>- db.engine.convert_unicode = True </del><ins>+ db.engine.dialect.convert_unicode = True </ins><span class="cx"> rawdata = 'Alors vous imaginez ma surprise, au lever du jour, quand une dr\xc3\xb4le de petit voix m\xe2\x80\x99a r\xc3\xa9veill\xc3\xa9. Elle disait: \xc2\xab S\xe2\x80\x99il vous pla\xc3\xaet\xe2\x80\xa6 dessine-moi un mouton! \xc2\xbb\n' </span><span class="cx"> unicodedata = rawdata.decode('utf-8') </span><span class="cx"> unicode_table.insert().execute(unicode_data=unicodedata, plain_data=rawdata) </span><span class="lines">@@ -131,7 +131,7 @@ </span><span class="cx"> self.assert_(isinstance(x['unicode_data'], unicode) and x['unicode_data'] == unicodedata) </span><span class="cx"> self.assert_(isinstance(x['plain_data'], unicode) and x['plain_data'] == unicodedata) </span><span class="cx"> finally: </span><del>- db.engine.convert_unicode = prev_unicode </del><ins>+ db.engine.dialect.convert_unicode = prev_unicode </ins><span class="cx"> </span><span class="cx"> class BinaryTest(AssertMixin): </span><span class="cx"> def setUpAll(self): </span><span class="lines">@@ -179,7 +179,7 @@ </span><span class="cx"> collist = [Column('user_id', INT, primary_key = True), Column('user_name', VARCHAR(20)), Column('user_datetime', DateTime), </span><span class="cx"> Column('user_date', Date), Column('user_time', Time)] </span><span class="cx"> </span><del>- if db.engine.__module__.endswith('mysql') or db.engine.__module__.endswith('mssql'): </del><ins>+ if db.engine.name == 'mysql' or db.engine.name == 'mssql': </ins><span class="cx"> # strip microseconds -- not supported by this engine (should be an easier way to detect this) </span><span class="cx"> for d in insert_data: </span><span class="cx"> if d[2] is not None: </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-23 23:55:17
|
<!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>[1328] sqlalchemy/branches/schema/test: starting to convert mapper.select() to session.query(class).select(), etc.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1328</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-23 18:55:03 -0500 (Sun, 23 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>starting to convert mapper.select() to session.query(class).select(), etc.</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormpropertiespy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormquerypy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormsessionpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py</a></li> <li><a href="#sqlalchemybranchesschematestmapperpy">sqlalchemy/branches/schema/test/mapper.py</a></li> <li><a href="#sqlalchemybranchesschematestobjectstorepy">sqlalchemy/branches/schema/test/objectstore.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemyormmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py (1327 => 1328)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-04-23 23:17:53 UTC (rev 1327) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-04-23 23:55:03 UTC (rev 1328) </span><span class="lines">@@ -12,7 +12,7 @@ </span><span class="cx"> import sqlalchemy.sql_util as sqlutil </span><span class="cx"> import sync </span><span class="cx"> from sqlalchemy.exceptions import * </span><del>-import query </del><ins>+import query as querylib </ins><span class="cx"> import session as sessionlib </span><span class="cx"> import sys </span><span class="cx"> import weakref </span><span class="lines">@@ -215,73 +215,75 @@ </span><span class="cx"> #for key, value in self.columntoproperty.iteritems(): </span><span class="cx"> # print key.table.name, key.key, [(v.key, v) for v in value] </span><span class="cx"> </span><del>- def _get_query(self): </del><ins>+ def using(self, session): + return querylib.Query(self, session=session) + def query(self, session=None): + """returns an instance of sqlalchemy.orm.query.Query, which implements all the query-constructing + methods such as get(), select(), select_by(), etc.""" + if session is not None: + return querylib.Query(self, session=session) + </ins><span class="cx"> try: </span><span class="cx"> if self._query.mapper is not self: </span><del>- self._query = query.Query(self) </del><ins>+ self._query = querylib.Query(self) </ins><span class="cx"> return self._query </span><span class="cx"> except AttributeError: </span><del>- self._query = query.Query(self) </del><ins>+ self._query = querylib.Query(self) </ins><span class="cx"> return self._query </span><del>- query = property(_get_query, doc=\ - """returns an instance of sqlalchemy.orm.query.Query, which implements all the query-constructing - methods such as get(), select(), select_by(), etc. The default Query object uses the global thread-local - Session from the session package. To get a Query object for a specific Session, call the - using(session) method.""") </del><span class="cx"> </span><span class="cx"> def get(self, *ident, **kwargs): </span><span class="cx"> """calls get() on this mapper's default Query object.""" </span><del>- return self.query.get(*ident, **kwargs) </del><ins>+ return self.query().get(*ident, **kwargs) </ins><span class="cx"> </span><span class="cx"> def _get(self, key, ident=None, reload=False): </span><del>- return self.query._get(key, ident=ident, reload=reload) </del><ins>+ return self.query()._get(key, ident=ident, reload=reload) </ins><span class="cx"> </span><span class="cx"> def get_by(self, *args, **params): </span><span class="cx"> """calls get_by() on this mapper's default Query object.""" </span><del>- return self.query.get_by(*args, **params) </del><ins>+ return self.query().get_by(*args, **params) </ins><span class="cx"> </span><span class="cx"> def select_by(self, *args, **params): </span><span class="cx"> """calls select_by() on this mapper's default Query object.""" </span><del>- return self.query.select_by(*args, **params) </del><ins>+ return self.query().select_by(*args, **params) </ins><span class="cx"> </span><span class="cx"> def selectfirst_by(self, *args, **params): </span><span class="cx"> """calls selectfirst_by() on this mapper's default Query object.""" </span><del>- return self.query.selectfirst_by(*args, **params) </del><ins>+ return self.query().selectfirst_by(*args, **params) </ins><span class="cx"> </span><span class="cx"> def selectone_by(self, *args, **params): </span><span class="cx"> """calls selectone_by() on this mapper's default Query object.""" </span><del>- return self.query.selectone_by(*args, **params) </del><ins>+ return self.query().selectone_by(*args, **params) </ins><span class="cx"> </span><span class="cx"> def count_by(self, *args, **params): </span><span class="cx"> """calls count_by() on this mapper's default Query object.""" </span><del>- return self.query.count_by(*args, **params) </del><ins>+ return self.query().count_by(*args, **params) </ins><span class="cx"> </span><span class="cx"> def selectfirst(self, *args, **params): </span><span class="cx"> """calls selectfirst() on this mapper's default Query object.""" </span><del>- return self.query.selectfirst(*args, **params) </del><ins>+ return self.query().selectfirst(*args, **params) </ins><span class="cx"> </span><span class="cx"> def selectone(self, *args, **params): </span><span class="cx"> """calls selectone() on this mapper's default Query object.""" </span><del>- return self.query.selectone(*args, **params) </del><ins>+ return self.query().selectone(*args, **params) </ins><span class="cx"> </span><span class="cx"> def select(self, arg=None, **kwargs): </span><span class="cx"> """calls select() on this mapper's default Query object.""" </span><del>- return self.query.select(arg=arg, **kwargs) </del><ins>+ return self.query().select(arg=arg, **kwargs) </ins><span class="cx"> </span><span class="cx"> def select_whereclause(self, whereclause=None, params=None, **kwargs): </span><span class="cx"> """calls select_whereclause() on this mapper's default Query object.""" </span><del>- return self.query.select_whereclause(whereclause=whereclause, params=params, **kwargs) </del><ins>+ return self.query().select_whereclause(whereclause=whereclause, params=params, **kwargs) </ins><span class="cx"> </span><span class="cx"> def count(self, whereclause=None, params=None, **kwargs): </span><span class="cx"> """calls count() on this mapper's default Query object.""" </span><del>- return self.query.count(whereclause=whereclause, params=params, **kwargs) </del><ins>+ return self.query().count(whereclause=whereclause, params=params, **kwargs) </ins><span class="cx"> </span><span class="cx"> def select_statement(self, statement, **params): </span><span class="cx"> """calls select_statement() on this mapper's default Query object.""" </span><del>- return self.query.select_statement(statement, **params) </del><ins>+ return self.query().select_statement(statement, **params) </ins><span class="cx"> </span><span class="cx"> def select_text(self, text, **params): </span><del>- return self.query.select_text(text, **params) </del><ins>+ return self.query().select_text(text, **params) </ins><span class="cx"> </span><span class="cx"> def add_properties(self, dict_of_properties): </span><span class="cx"> """adds the given dictionary of properties to this mapper, using add_property.""" </span><span class="lines">@@ -430,7 +432,7 @@ </span><span class="cx"> def compile(self, whereclause = None, **options): </span><span class="cx"> """works like select, except returns the SQL statement object without </span><span class="cx"> compiling or executing it""" </span><del>- return self.query._compile(whereclause, **options) </del><ins>+ return self.query()._compile(whereclause, **options) </ins><span class="cx"> </span><span class="cx"> def copy(self, **kwargs): </span><span class="cx"> mapper = Mapper.__new__(Mapper) </span><span class="lines">@@ -439,9 +441,7 @@ </span><span class="cx"> mapper.props = self.props.copy() </span><span class="cx"> return mapper </span><span class="cx"> </span><del>- def using(self, session): - return query.Query(self, session=session) - </del><ins>+ </ins><span class="cx"> def options(self, *options, **kwargs): </span><span class="cx"> """uses this mapper as a prototype for a new mapper with different behavior. </span><span class="cx"> *options is a list of options directives, which include eagerload(), lazyload(), and noload()""" </span><span class="lines">@@ -458,7 +458,7 @@ </span><span class="cx"> </span><span class="cx"> def __getattr__(self, key): </span><span class="cx"> if (key.startswith('select_by_') or key.startswith('get_by_')): </span><del>- return getattr(self.query, key) </del><ins>+ return getattr(self.query(), key) </ins><span class="cx"> else: </span><span class="cx"> raise AttributeError(key) </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py (1327 => 1328)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py 2006-04-23 23:17:53 UTC (rev 1327) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py 2006-04-23 23:55:03 UTC (rev 1328) </span><span class="lines">@@ -362,7 +362,7 @@ </span><span class="cx"> (self.lazywhere, self.lazybinds) = create_lazy_clause(self.parent.noninherited_table, self.primaryjoin, self.secondaryjoin, self.foreignkey) </span><span class="cx"> # determine if our "lazywhere" clause is the same as the mapper's </span><span class="cx"> # get() clause. then we can just use mapper.get() </span><del>- self.use_get = not self.uselist and self.mapper.query._get_clause.compare(self.lazywhere) </del><ins>+ self.use_get = not self.uselist and self.mapper.query()._get_clause.compare(self.lazywhere) </ins><span class="cx"> </span><span class="cx"> def _set_class_attribute(self, class_, key): </span><span class="cx"> # establish a class-level lazy loader on our class </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormquerypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py (1327 => 1328)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py 2006-04-23 23:17:53 UTC (rev 1327) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py 2006-04-23 23:55:03 UTC (rev 1328) </span><span class="lines">@@ -137,6 +137,7 @@ </span><span class="cx"> statement = self._compile(whereclause, **kwargs) </span><span class="cx"> return self._select_statement(statement, params=params) </span><span class="cx"> </span><ins>+ </ins><span class="cx"> def count(self, whereclause=None, params=None, **kwargs): </span><span class="cx"> s = self.table.count(whereclause) </span><span class="cx"> if params is not None: </span><span class="lines">@@ -151,6 +152,10 @@ </span><span class="cx"> t = sql.text(text) </span><span class="cx"> return self.instances(t, params=params) </span><span class="cx"> </span><ins>+ def options(self, *args, **kwargs): + """returns a new Query object using the given MapperOptions.""" + return self.mapper.options(*args, **kwargs).using(session=self._session) + </ins><span class="cx"> def __getattr__(self, key): </span><span class="cx"> if (key.startswith('select_by_')): </span><span class="cx"> key = key[10:] </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormsessionpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py (1327 => 1328)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py 2006-04-23 23:17:53 UTC (rev 1327) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py 2006-04-23 23:55:03 UTC (rev 1328) </span><span class="lines">@@ -11,7 +11,6 @@ </span><span class="cx"> import sqlalchemy </span><span class="cx"> import sqlalchemy.sql as sql </span><span class="cx"> </span><del>- </del><span class="cx"> class SessionTransaction(object): </span><span class="cx"> def __init__(self, session, parent=None, autoflush=True): </span><span class="cx"> self.session = session </span><span class="lines">@@ -56,6 +55,7 @@ </span><span class="cx"> self.session.transaction = None </span><span class="cx"> </span><span class="cx"> class Session(object): </span><ins>+ """encapsulates a set of objects being operated upon within an object-relational operation.""" </ins><span class="cx"> def __init__(self, bind_to=None, hash_key=None, new_imap=True, import_session=None): </span><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="lines">@@ -113,6 +113,9 @@ </span><span class="cx"> """ </span><span class="cx"> if self.transaction is not None: </span><span class="cx"> self.transaction.close() </span><ins>+ def mapper(self, class_, entity_name=None): + """given an Class, returns the primary Mapper responsible for persisting it""" + return class_mapper(class_, entity_name = entity_name) </ins><span class="cx"> def bind_mapper(self, mapper, bindto): </span><span class="cx"> """binds the given Mapper to the given Engine or Connection. All subsequent operations involving this </span><span class="cx"> Mapper will use the given bindto.""" </span></span></pre></div> <a id="sqlalchemybranchesschematestmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/mapper.py (1327 => 1328)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/mapper.py 2006-04-23 23:17:53 UTC (rev 1327) +++ sqlalchemy/branches/schema/test/mapper.py 2006-04-23 23:55:03 UTC (rev 1328) </span><span class="lines">@@ -75,28 +75,30 @@ </span><span class="cx"> </span><span class="cx"> class MapperTest(MapperSuperTest): </span><span class="cx"> def testget(self): </span><del>- m = mapper(User, users) - self.assert_(m.get(19) is None) - u = m.get(7) - u2 = m.get(7) </del><ins>+ s = create_session() + mapper(User, users) + self.assert_(s.get(User, 19) is None) + u = s.get(User, 7) + u2 = s.get(User, 7) </ins><span class="cx"> self.assert_(u is u2) </span><del>- objectstore.clear() - u2 = m.get(7) </del><ins>+ s.clear() + u2 = s.get(User, 7) </ins><span class="cx"> self.assert_(u is not u2) </span><span class="cx"> </span><span class="cx"> def testrefresh(self): </span><del>- m = mapper(User, users, properties={'addresses':relation(mapper(Address, addresses))}) - u = m.get(7) </del><ins>+ mapper(User, users, properties={'addresses':relation(mapper(Address, addresses))}) + s = create_session() + u = s.get(User, 7) </ins><span class="cx"> u.user_name = 'foo' </span><span class="cx"> a = Address() </span><span class="cx"> u.addresses.append(a) </span><span class="cx"> </span><span class="cx"> self.assert_(a in u.addresses) </span><span class="cx"> </span><del>- objectstore.refresh(u) </del><ins>+ s.refresh(u) </ins><span class="cx"> </span><span class="cx"> # its refreshed, so not dirty </span><del>- self.assert_(u not in objectstore.get_session().uow.dirty) </del><ins>+ self.assert_(u not in s.dirty) </ins><span class="cx"> </span><span class="cx"> # username is back to the DB </span><span class="cx"> self.assert_(u.user_name == 'jack') </span><span class="lines">@@ -106,10 +108,10 @@ </span><span class="cx"> u.user_name = 'foo' </span><span class="cx"> u.addresses.append(a) </span><span class="cx"> # now its dirty </span><del>- self.assert_(u in objectstore.get_session().uow.dirty) </del><ins>+ self.assert_(u in s.dirty) </ins><span class="cx"> self.assert_(u.user_name == 'foo') </span><span class="cx"> self.assert_(a in u.addresses) </span><del>- objectstore.expire(u) </del><ins>+ s.expire(u) </ins><span class="cx"> </span><span class="cx"> # get the attribute, it refreshes </span><span class="cx"> self.assert_(u.user_name == 'jack') </span><span class="lines">@@ -117,27 +119,29 @@ </span><span class="cx"> </span><span class="cx"> def testrefresh_lazy(self): </span><span class="cx"> """tests that when a lazy loader is set as a trigger on an object's attribute (at the attribute level, not the class level), a refresh() operation doesnt fire the lazy loader or create any problems""" </span><del>- m = mapper(User, users, properties={'addresses':relation(mapper(Address, addresses))}) - m2 = m.options(lazyload('addresses')) - u = m2.selectfirst(users.c.user_id==8) </del><ins>+ s = create_session() + mapper(User, users, properties={'addresses':relation(mapper(Address, addresses))}) + q2 = s.query(User).options(lazyload('addresses')) + u = q2.selectfirst(users.c.user_id==8) </ins><span class="cx"> def go(): </span><del>- objectstore.refresh(u) </del><ins>+ s.refresh(u) </ins><span class="cx"> self.assert_sql_count(db, go, 1) </span><span class="cx"> </span><span class="cx"> def testsessionpropigation(self): </span><span class="cx"> sess = create_session() </span><del>- m = mapper(User, users, properties={'addresses':relation(mapper(Address, addresses), lazy=True)}) - u = m.using(sess).get(7) - assert objectstore.get_session(u) is sess - assert objectstore.get_session(u.addresses[0]) is sess </del><ins>+ mapper(User, users, properties={'addresses':relation(mapper(Address, addresses), lazy=True)}) + u = sess.get(User, 7) + assert get_session(u) is sess + assert get_session(u.addresses[0]) is sess </ins><span class="cx"> </span><span class="cx"> def testexpire(self): </span><del>- m = mapper(User, users, properties={'addresses':relation(mapper(Address, addresses), lazy=False)}) - u = m.get(7) </del><ins>+ s = create_session() + mapper(User, users, properties={'addresses':relation(mapper(Address, addresses), lazy=False)}) + u = s.get(User, 7) </ins><span class="cx"> assert(len(u.addresses) == 1) </span><span class="cx"> u.user_name = 'foo' </span><span class="cx"> del u.addresses[0] </span><del>- objectstore.expire(u) </del><ins>+ s.expire(u) </ins><span class="cx"> # test plain expire </span><span class="cx"> self.assert_(u.user_name =='jack') </span><span class="cx"> self.assert_(len(u.addresses) == 1) </span><span class="lines">@@ -145,14 +149,14 @@ </span><span class="cx"> # we're changing the database here, so if this test fails in the middle, </span><span class="cx"> # it'll screw up the other tests which are hardcoded to 7/'jack' </span><span class="cx"> u.user_name = 'foo' </span><del>- objectstore.flush() </del><ins>+ s.flush() </ins><span class="cx"> # change the value in the DB </span><span class="cx"> users.update(users.c.user_id==7, values=dict(user_name='jack')).execute() </span><del>- objectstore.expire(u) </del><ins>+ s.expire(u) </ins><span class="cx"> # object isnt refreshed yet, using dict to bypass trigger </span><span class="cx"> self.assert_(u.__dict__['user_name'] != 'jack') </span><span class="cx"> # do a select </span><del>- m.select() </del><ins>+ s.query(User).select() </ins><span class="cx"> # test that it refreshed </span><span class="cx"> self.assert_(u.__dict__['user_name'] == 'jack') </span><span class="cx"> </span><span class="lines">@@ -161,25 +165,26 @@ </span><span class="cx"> self.assert_(u.user_name =='jack') </span><span class="cx"> </span><span class="cx"> def testrefresh2(self): </span><del>- assign_mapper(Address, addresses) </del><ins>+ s = create_session() + mapper(Address, addresses) </ins><span class="cx"> </span><del>- assign_mapper(User, users, properties = dict(addresses=relation(Address.mapper,private=True,lazy=False)) ) </del><ins>+ mapper(User, users, properties = dict(addresses=relation(Address,private=True,lazy=False)) ) </ins><span class="cx"> </span><span class="cx"> u=User() </span><span class="cx"> u.user_name='Justin' </span><span class="cx"> a = Address() </span><span class="cx"> a.address_id=17 # to work around the hardcoded IDs in this test suite.... </span><span class="cx"> u.addresses.append(a) </span><del>- objectstore.flush() - objectstore.clear() - u = User.mapper.selectfirst() </del><ins>+ s.flush() + s.clear() + u = s.query(User).selectfirst() </ins><span class="cx"> print u.user_name </span><span class="cx"> </span><span class="cx"> #ok so far </span><del>- u.expire() #hangs when </del><ins>+ s.expire(u) #hangs when </ins><span class="cx"> print u.user_name #this line runs </span><span class="cx"> </span><del>- u.refresh() #hangs </del><ins>+ s.refresh(u) #hangs </ins><span class="cx"> </span><span class="cx"> def testmagic(self): </span><span class="cx"> m = mapper(User, users, properties = { </span></span></pre></div> <a id="sqlalchemybranchesschematestobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/objectstore.py (1327 => 1328)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/objectstore.py 2006-04-23 23:17:53 UTC (rev 1327) +++ sqlalchemy/branches/schema/test/objectstore.py 2006-04-23 23:55:03 UTC (rev 1328) </span><span class="lines">@@ -147,8 +147,10 @@ </span><span class="cx"> </span><span class="cx"> def testbasic(self): </span><span class="cx"> class Test(object): </span><del>- pass - assign_mapper(Test, uni_table) </del><ins>+ def __init__(self, id, txt): + self.id = id + self.txt = txt + mapper(Test, uni_table) </ins><span class="cx"> </span><span class="cx"> txt = u"\u0160\u0110\u0106\u010c\u017d" </span><span class="cx"> t1 = Test(id=1, txt = txt) </span></span></pre> </div> </div> </body> </html> |
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> |
From: <co...@sq...> - 2006-04-23 22:19:18
|
<!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>[1326] sqlalchemy/branches/schema/test: session methods become single argument, less ambiguous</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1326</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-23 17:19:06 -0500 (Sun, 23 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>session methods become single argument, less ambiguous</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemymodsthreadlocalpy">sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.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="#sqlalchemybranchesschematestobjectstorepy">sqlalchemy/branches/schema/test/objectstore.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemymodsthreadlocalpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py (1325 => 1326)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py 2006-04-23 21:41:36 UTC (rev 1325) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py 2006-04-23 22:19:06 UTC (rev 1326) </span><span class="lines">@@ -22,13 +22,13 @@ </span><span class="cx"> get_session = session.get_session </span><span class="cx"> </span><span class="cx"> class Objectstore(object): </span><del>- def begin(self, *obj): - return get_session().begin(*obj) - def commit(self, *obj): - return get_session().commit(*obj) </del><ins>+ def begin(self, obj): + return get_session().begin(obj) + def commit(self, obj): + return get_session().commit(obj) </ins><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): </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">@@ -36,7 +36,7 @@ </span><span class="cx"> </span><span class="cx"> if individual objects are submitted, then only those objects are committed, and the </span><span class="cx"> begin/commit cycle is not affected.""" </span><del>- get_session().flush(*obj) </del><ins>+ get_session().flush(obj) </ins><span class="cx"> </span><span class="cx"> def clear(self): </span><span class="cx"> """removes all current UnitOfWorks and IdentityMaps for this thread and </span><span class="lines">@@ -44,22 +44,22 @@ </span><span class="cx"> current mapped object instances, as they are no longer in the Identity Map.""" </span><span class="cx"> get_session().clear() </span><span class="cx"> </span><del>- def refresh(self, *obj): </del><ins>+ def refresh(self, obj): </ins><span class="cx"> """reloads the state of this object from the database, and cancels any in-memory </span><span class="cx"> changes.""" </span><del>- get_session().refresh(*obj) </del><ins>+ get_session().refresh(obj) </ins><span class="cx"> </span><del>- def expire(self, *obj): </del><ins>+ def expire(self, obj): </ins><span class="cx"> """invalidates the data in the given objects and sets them to refresh themselves </span><span class="cx"> the next time they are requested.""" </span><del>- get_session().expire(*obj) </del><ins>+ get_session().expire(obj) </ins><span class="cx"> </span><del>- def expunge(self, *obj): - get_session().expunge(*obj) </del><ins>+ def expunge(self, obj): + get_session().expunge(obj) </ins><span class="cx"> </span><del>- def delete(self, *obj): </del><ins>+ def delete(self, obj): </ins><span class="cx"> """registers the given objects as to be deleted upon the next commit""" </span><del>- s = get_session().delete(*obj) </del><ins>+ s = get_session().delete(obj) </ins><span class="cx"> </span><span class="cx"> def has_key(self, key): </span><span class="cx"> """returns True if the current thread-local IdentityMap contains the given instance key""" </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.select </del><ins>+ class_.select = m.selectobj </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="sqlalchemybranchesschemalibsqlalchemyormmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py (1325 => 1326)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-04-23 21:41:36 UTC (rev 1325) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-04-23 22:19:06 UTC (rev 1326) </span><span class="lines">@@ -978,11 +978,11 @@ </span><span class="cx"> else: </span><span class="cx"> return repr(obj) </span><span class="cx"> </span><del>-def object_mapper(object, raiseerror=True): </del><ins>+def object_mapper(object, raiseerror=True, entity_name=None): </ins><span class="cx"> """given an object, returns the primary Mapper associated with the object </span><span class="cx"> or the object's class.""" </span><span class="cx"> try: </span><del>- return mapper_registry[ClassKey(object.__class__, getattr(object, '_entity_name', None))] </del><ins>+ return mapper_registry[ClassKey(object.__class__, getattr(object, '_entity_name', entity_name))] </ins><span class="cx"> except KeyError: </span><span class="cx"> if raiseerror: </span><span class="cx"> raise InvalidRequestError("Class '%s' entity name '%s' has no mapper associated with it" % (object.__class__.__name__, getattr(object, '_entity_name', None))) </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormsessionpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py (1325 => 1326)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py 2006-04-23 21:41:36 UTC (rev 1325) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py 2006-04-23 22:19:06 UTC (rev 1326) </span><span class="lines">@@ -204,61 +204,87 @@ </span><span class="cx"> """deprecated""" </span><span class="cx"> raise InvalidRequestError("Session.commit() is deprecated. use install_mod('legacy_session') to enable the old behavior") </span><span class="cx"> </span><del>- def flush(self, *obj): - """flushes all the object modifications present in this session to the database. if object - arguments are given, then only those objects (and immediate dependencies) are flushed.""" - self.uow.flush(self, *obj) - </del><ins>+ def flush(self, objects=None): + """flushes all the object modifications present in this session to the database. 'objects' + is a list or tuple of objects specifically to be flushed.""" + self.uow.flush(self, objects) + + def get(self, class_, *ident): + """given a class and a primary key identifier, loads the corresponding object.""" + return self.query(class_).get(*ident) + </ins><span class="cx"> def load(self, class_, *ident): </span><span class="cx"> """given a class and a primary key identifier, loads the corresponding object.""" </span><span class="cx"> return self.query(class_).get(*ident) </span><span class="cx"> </span><del>- def refresh(self, *obj): - """reloads the attributes for the given objects from the database, clears </del><ins>+ def refresh(self, object): + """reloads the attributes for the given object from the database, clears </ins><span class="cx"> any changes made.""" </span><del>- for o in obj: - self.uow.refresh(o) </del><ins>+ self.uow.refresh(object) </ins><span class="cx"> </span><del>- def expire(self, *obj): - """invalidates the data in the given objects and sets them to refresh themselves </del><ins>+ def expire(self, object): + """invalidates the data in the given object and sets them to refresh themselves </ins><span class="cx"> the next time they are requested.""" </span><del>- for o in obj: - self.uow.expire(o) </del><ins>+ self.uow.expire(object) </ins><span class="cx"> </span><del>- def expunge(self, *obj): - """removes the given objects from this Session. this will free all internal references to the objects.""" - for o in obj: - self.uow.expunge(o) </del><ins>+ def expunge(self, object): + """removes the given object from this Session. this will free all internal references to the object.""" + self.uow.expunge(object) </ins><span class="cx"> </span><del>- def save(self, *obj, **kwargs): - """adds unsaved objects to this Session. </del><ins>+ def save(self, object, entity_name=None): + """adds an unsaved object to this Session. </ins><span class="cx"> </span><span class="cx"> The 'entity_name' keyword argument can also be given which will be assigned </span><span class="cx"> to the instances if given. </span><span class="cx"> """ </span><del>- for o in obj: - for c in object_mapper(o, **kwargs).cascade_iterator('save-update', o): - if c is o: - self._save_impl(c, **kwargs) - else: - self.save_or_update(c, **kwargs) </del><ins>+ for c in object_mapper(object, entity_name=entity_name).cascade_iterator('save-update', object): + if c is object: + self._save_impl(c, entity_name=entity_name) + else: + self.save_or_update(c, entity_name=entity_name) </ins><span class="cx"> </span><del>- def update(self, *obj, **kwargs): - for o in obj: - for c in object_mapper(o, **kwargs).cascade_iterator('save-update', o): - if c is o: - self._update_impl(c, **kwargs) - else: - self.save_or_update(c, **kwargs) </del><ins>+ def update(self, object, entity_name=None): + for c in object_mapper(object, entity_name=entity_name).cascade_iterator('save-update', object): + if c is o: + self._update_impl(c, entity_name=entity_name) + else: + self.save_or_update(c, entity_name=entity_name) </ins><span class="cx"> </span><del>- def save_or_update(self, *obj, **kwargs): - for o in obj: - for c in object_mapper(o, *kwargs).cascade_iterator('save-update', o): - key = getattr(o, '_instance_key', None) - if key is None: - self._save_impl(c, **kwargs) - else: - self._update_impl(c, **kwargs) </del><ins>+ def save_or_update(self, object, entity_name=None): + for c in object_mapper(object, entity_name=entity_name).cascade_iterator('save-update', object): + key = getattr(object, '_instance_key', None) + if key is None: + self._save_impl(c, entity_name=entity_name) + else: + self._update_impl(c, entity_name=entity_name) + + def clear(self): + """removes all object instances from this Session. this is equivalent to calling expunge() for all + objects in this Session.""" + self.uow = unitofwork.UnitOfWork() + + def delete(self, object, entity_name=None): + for c in object_mapper(object, entity_name=entity_name).cascade_iterator('delete', object): + self.uow.register_deleted(c) + + def merge(self, object, entity_name=None): + for obj in object_mapper(object, entity_name=entity_name).cascade_iterator('merge', object): + key = getattr(obj, '_instance_key', None) + if key is None: + mapper = object_mapper(object, entity_name=entity_name) + ident = mapper.identity(object) + for k in ident: + if k is None: + raise InvalidRequestError("Instance '%s' does not have a full set of identity values, and does not represent a saved entity in the database. Use the add() method to add unsaved instances to this Session." % str(obj)) + key = mapper.identity_key(*ident) + u = self.uow + if u.identity_map.has_key(key): + return u.identity_map[key] + else: + obj._instance_key = key + u.identity_map[key] = obj + self._bind_to(instance) + return instance </ins><span class="cx"> </span><span class="cx"> def _save_impl(self, object, **kwargs): </span><span class="cx"> if hasattr(object, '_instance_key'): </span><span class="lines">@@ -293,9 +319,9 @@ </span><span class="cx"> def _register_deleted(self, obj): </span><span class="cx"> self._bind_to(obj) </span><span class="cx"> self.uow.register_deleted(obj) </span><ins>+ </ins><span class="cx"> def _bind_to(self, obj): </span><del>- """given an object, binds it to this session. changes on the object will affect - the currently scoped UnitOfWork maintained by this session.""" </del><ins>+ """given an object, binds it to this session. """ </ins><span class="cx"> if getattr(obj, '_sa_session_id', None) != self.hash_key: </span><span class="cx"> old = getattr(obj, '_sa_session_id', None) </span><span class="cx"> # remove from old session. we do this gingerly since _sessions is a WeakValueDict </span><span class="lines">@@ -307,6 +333,9 @@ </span><span class="cx"> sess = None </span><span class="cx"> if sess is not None: </span><span class="cx"> sess.expunge(old) </span><ins>+ key = getattr(obj, '_instance_key', None) + if key is not None: + self.identity_map[key] = obj </ins><span class="cx"> obj._sa_session_id = self.hash_key </span><span class="cx"> def _is_bound(self, obj): </span><span class="cx"> return getattr(obj, '_sa_session_id', None) == self.hash_key </span><span class="lines">@@ -320,71 +349,14 @@ </span><span class="cx"> def is_expired(self, instance, **kwargs): </span><span class="cx"> return self.uow.is_expired(instance, **kwargs) </span><span class="cx"> </span><del>- dirty = property(lambda s:s.uow.dirty) - deleted = property(lambda s:s.uow.deleted) - new = property(lambda s:s.uow.new) - identity_map = property(lambda s:s.uow.identity_map) </del><ins>+ dirty = property(lambda s:s.uow.dirty, doc="a Set of all objects marked as 'dirty' within this Session") + deleted = property(lambda s:s.uow.deleted, doc="a Set of all objects marked as 'deleted' within this Session") + new = property(lambda s:s.uow.new, doc="a Set of all objects marked as 'new' within this Session.") + identity_map = property(lambda s:s.uow.identity_map, doc="a WeakValueDictionary consisting of all objects within this Session keyed to their _instance_key value.") </ins><span class="cx"> </span><del>- def clear(self): - """removes all object instances from this Session. this is equivalent to calling expunge() for all - objects in this Session.""" - self.uow = unitofwork.UnitOfWork() - - def delete(self, *obj, **kwargs): - """registers the given objects to be deleted upon the next flush(). If the given objects are not part of this - Session, they will be imported. the objects are expected to either have an _instance_key - attribute or have all of their primary key attributes populated. - - the keyword argument 'entity_name' can also be provided which will be used by the import.""" - for o in obj: - for c in object_mapper(o, **kwargs).cascade_iterator('delete', o): - if not self._is_bound(c): - c = self.import_(c, **kwargs) - self.uow.register_deleted(c) - - - def merge(self, instance, entity_name=None): - """given an instance that represents a saved item, adds it to this session. - the return value is either the given instance, or if an instance corresponding to the - identity of the given instance already exists within this session, then that instance is returned; - the returned instance should always be used following this method. - - if the given instance does not have an _instance_key and also does not have all - of its primary key attributes populated, an exception is raised. similarly, if no - mapper can be located for the given instance, an exception is raised. - - this method should be used for any object instance that is coming from a serialized - storage, or was loaded by a Session other than this one. - - the keyword parameter entity_name is optional and is used to locate a Mapper for this - class which also specifies the given entity name. - """ - if instance is None: - return None - key = getattr(object, '_instance_key', None) - if key is None: - mapper = object_mapper(object, raiseerror=False) - if mapper is None: - mapper = class_mapper(object, entity_name=entity_name) - ident = mapper.identity(object) - for k in ident: - if k is None: - if raiseerror: - raise InvalidRequestError("Instance '%s' does not have a full set of identity values, and does not represent a saved entity in the database. Use the add() method to add unsaved instances to this Session." % str(object)) - else: - return None - key = mapper.identity_key(*ident) - u = self.uow - if u.identity_map.has_key(key): - return u.identity_map[key] - else: - instance._instance_key = key - u.identity_map[key] = instance - self._bind_to(instance) - return instance </del><span class="cx"> </span><span class="cx"> def import_instance(self, *args, **kwargs): </span><del>- """deprecated; a synynom for import()""" </del><ins>+ """deprecated; a synynom for merge()""" </ins><span class="cx"> return self.merge(*args, **kwargs) </span><span class="cx"> </span><span class="cx"> def get_id_key(ident, class_, entity_name=None): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormunitofworkpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/unitofwork.py (1325 => 1326)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/unitofwork.py 2006-04-23 21:41:36 UTC (rev 1325) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/unitofwork.py 2006-04-23 22:19:06 UTC (rev 1326) </span><span class="lines">@@ -214,10 +214,10 @@ </span><span class="cx"> except KeyError: </span><span class="cx"> pass </span><span class="cx"> </span><del>- def flush(self, session, *objects): </del><ins>+ def flush(self, session, objects=None): </ins><span class="cx"> flush_context = UOWTransaction(self, session) </span><span class="cx"> </span><del>- if len(objects): </del><ins>+ if objects is not None: </ins><span class="cx"> objset = util.HashSet(iter=objects) </span><span class="cx"> else: </span><span class="cx"> objset = None </span></span></pre></div> <a id="sqlalchemybranchesschematestobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/objectstore.py (1325 => 1326)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/objectstore.py 2006-04-23 21:41:36 UTC (rev 1325) +++ sqlalchemy/branches/schema/test/objectstore.py 2006-04-23 22:19:06 UTC (rev 1326) </span><span class="lines">@@ -62,7 +62,8 @@ </span><span class="cx"> </span><span class="cx"> u = User() </span><span class="cx"> a = Address() </span><del>- s.save(u, a) </del><ins>+ s.save(u) + s.save(a) </ins><span class="cx"> a.user = u </span><span class="cx"> #print repr(a.__class__._attribute_manager.get_history(a, 'user').added_items()) </span><span class="cx"> #print repr(u.addresses.added_items()) </span><span class="lines">@@ -268,10 +269,10 @@ </span><span class="cx"> a.bs.append(b2) </span><span class="cx"> </span><span class="cx"> # inserts both A and Bs </span><del>- objectstore.flush(a) </del><ins>+ objectstore.flush([a]) </ins><span class="cx"> </span><span class="cx"> objectstore.delete(a) </span><del>- objectstore.flush(a) </del><ins>+ objectstore.flush([a]) </ins><span class="cx"> </span><span class="cx"> assert b_table.count().scalar() == 0 </span><span class="cx"> </span><span class="lines">@@ -421,7 +422,7 @@ </span><span class="cx"> </span><span class="cx"> objectstore.get_session().save(u) </span><span class="cx"> </span><del>- objectstore.get_session().flush(u) </del><ins>+ objectstore.get_session().flush([u]) </ins><span class="cx"> objectstore.get_session().flush() </span><span class="cx"> </span><span class="cx"> # assert the first one retreives the same from the identity map </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-23 21:41:45
|
<!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>[1325] sqlalchemy/trunk/test: Got the unit tests running again, apart from the two that were not working in</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1325</dd> <dt>Author</dt> <dd>cleverdevil</dd> <dt>Date</dt> <dd>2006-04-23 16:41:36 -0500 (Sun, 23 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>Got the unit tests running again, apart from the two that were not working in the first place. The changes to process_relationships and to sqlachemy itself were causing a double 'assign_mapper' call to cause issues. Now, we basically defer calling assign_mapper until process_relationships in cases where there are defined relationships. Also, I moved ActiveMapper to always use the default engine, as there were a lot of hacks inside ActiveMapper to allow for engine swapping. The use of the default engine and the "global_connect" functionality significantly improves the usability of ActiveMapper. ActiveMapper will be getting a bit of a refactor/cleanup at some point in the nearish future, as it has drifted a bit to complexity with the addition of some features. For now, this should do the trick!</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyextactivemapperpy">sqlalchemy/trunk/lib/sqlalchemy/ext/activemapper.py</a></li> <li><a href="#sqlalchemytrunktestactivemapperpy">sqlalchemy/trunk/test/activemapper.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemyextactivemapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/ext/activemapper.py (1324 => 1325)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/ext/activemapper.py 2006-04-23 21:05:13 UTC (rev 1324) +++ sqlalchemy/trunk/lib/sqlalchemy/ext/activemapper.py 2006-04-23 21:41:36 UTC (rev 1325) </span><span class="lines">@@ -1,19 +1,10 @@ </span><del>-from sqlalchemy import objectstore, create_engine, assign_mapper, relation, mapper, join -from sqlalchemy import and_, or_ </del><ins>+from sqlalchemy import assign_mapper, relation, exceptions </ins><span class="cx"> from sqlalchemy import Table, Column, ForeignKey </span><del>-from sqlalchemy.ext.proxy import ProxyEngine </del><span class="cx"> </span><span class="cx"> import inspect </span><span class="cx"> import sys </span><span class="cx"> </span><span class="cx"> # </span><del>-# the "proxy" to the database engine... this can be swapped out at runtime -# -engine = ProxyEngine() - - - -# </del><span class="cx"> # declarative column declaration - this is so that we can infer the colname </span><span class="cx"> # </span><span class="cx"> class column(object): </span><span class="lines">@@ -87,22 +78,21 @@ </span><span class="cx"> def process_relationships(klass): </span><span class="cx"> defer = False </span><span class="cx"> for propname, reldesc in klass.relations.items(): </span><del>- #We require that every related table has been processed first </del><ins>+ # we require that every related table has been processed first </ins><span class="cx"> if not reldesc.classname in __processed_classes__: </span><span class="cx"> if not klass._classname in __deferred_classes__: __deferred_classes__.append(klass._classname) </span><span class="cx"> defer = True </span><del>- - - #Check every column item to see if it points to an existing table - #if it does not, defer... </del><ins>+ + # check every column item to see if it points to an existing table + # if it does not, defer... </ins><span class="cx"> if not defer: </span><del>- if not check_relationships(klass): - if not klass._classname in __deferred_classes__: __deferred_classes__.append(klass._classname) - defer = True - </del><ins>+ if not check_relationships(klass): + if not klass._classname in __deferred_classes__: __deferred_classes__.append(klass._classname) + defer = True + </ins><span class="cx"> if not defer: </span><span class="cx"> relations = {} </span><del>- __processed_classes__.append(klass._classname) </del><ins>+ </ins><span class="cx"> for propname, reldesc in klass.relations.items(): </span><span class="cx"> relclass = ActiveMapperMeta.classes[reldesc.classname] </span><span class="cx"> relations[propname] = relation(relclass.mapper, </span><span class="lines">@@ -111,17 +101,23 @@ </span><span class="cx"> private=reldesc.private, </span><span class="cx"> lazy=reldesc.lazy, </span><span class="cx"> uselist=reldesc.uselist) </span><del>- if len(relations)>0: - assign_mapper(klass, klass.table, properties=relations) </del><ins>+ if len(relations) > 0: + assign_ok = True + try: + assign_mapper(klass, klass.table, properties=relations) + except exceptions.ArgumentError: + assign_ok = False </ins><span class="cx"> </span><del>- if klass._classname in __deferred_classes__: __deferred_classes__.remove(klass._classname) </del><ins>+ if assign_ok: + __processed_classes__.append(klass._classname) + if klass._classname in __deferred_classes__: __deferred_classes__.remove(klass._classname) + else: + __processed_classes__.append(klass._classname) </ins><span class="cx"> </span><span class="cx"> for deferred_class in __deferred_classes__: </span><span class="cx"> process_relationships(ActiveMapperMeta.classes[deferred_class]) </span><span class="cx"> </span><span class="cx"> </span><del>- - </del><span class="cx"> class ActiveMapperMeta(type): </span><span class="cx"> classes = {} </span><span class="cx"> </span><span class="lines">@@ -129,7 +125,6 @@ </span><span class="cx"> table_name = clsname.lower() </span><span class="cx"> columns = [] </span><span class="cx"> relations = {} </span><del>- _engine = getattr( sys.modules[cls.__module__], "__engine__", engine ) </del><span class="cx"> </span><span class="cx"> if 'mapping' in dict: </span><span class="cx"> members = inspect.getmembers(dict.get('mapping')) </span><span class="lines">@@ -138,10 +133,6 @@ </span><span class="cx"> table_name = value </span><span class="cx"> continue </span><span class="cx"> </span><del>- if '__engine__' == name: - _engine= value - continue - </del><span class="cx"> if name.startswith('__'): continue </span><span class="cx"> </span><span class="cx"> if isinstance(value, column): </span><span class="lines">@@ -161,13 +152,14 @@ </span><span class="cx"> </span><span class="cx"> if isinstance(value, relationship): </span><span class="cx"> relations[name] = value </span><del>- assert _engine is not None, "No engine specified" - cls.table = Table(table_name, _engine, *columns) </del><ins>+ + cls.table = Table(table_name, redefine=True, *columns) + </ins><span class="cx"> # check for inheritence </span><del>- if hasattr( bases[0], "mapping" ): - cls._base_mapper= bases[0].mapper </del><ins>+ if hasattr(bases[0], "mapping"): + cls._base_mapper = bases[0].mapper </ins><span class="cx"> assign_mapper(cls, cls.table, inherits=cls._base_mapper) </span><del>- else: </del><ins>+ elif len(relations) == 0: </ins><span class="cx"> assign_mapper(cls, cls.table) </span><span class="cx"> cls.relations = relations </span><span class="cx"> cls._classname = clsname </span><span class="lines">@@ -191,4 +183,11 @@ </span><span class="cx"> </span><span class="cx"> def create_tables(): </span><span class="cx"> for klass in ActiveMapperMeta.classes.values(): </span><del>- klass.table.create() </del><span class="cx">\ No newline at end of file </span><ins>+ klass.table.create() + +# +# a utility function to drop all tables for all ActiveMapper classes +# +def drop_tables(): + for klass in ActiveMapperMeta.classes.values(): + klass.table.drop() </ins><span class="cx">\ No newline at end of file </span></span></pre></div> <a id="sqlalchemytrunktestactivemapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/activemapper.py (1324 => 1325)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/activemapper.py 2006-04-23 21:05:13 UTC (rev 1324) +++ sqlalchemy/trunk/test/activemapper.py 2006-04-23 21:41:36 UTC (rev 1325) </span><span class="lines">@@ -1,11 +1,11 @@ </span><del>-from activemapper import ActiveMapper, column, one_to_many, one_to_one -from sqlalchemy import objectstore -from sqlalchemy import and_, or_ -from sqlalchemy import ForeignKey, String, Integer, DateTime -from datetime import datetime </del><ins>+from sqlalchemy.ext.activemapper import ActiveMapper, column, one_to_many, one_to_one +from sqlalchemy.ext import activemapper +from sqlalchemy import objectstore, global_connect +from sqlalchemy import and_, or_ +from sqlalchemy import ForeignKey, String, Integer, DateTime +from datetime import datetime </ins><span class="cx"> </span><span class="cx"> import unittest </span><del>-import activemapper </del><span class="cx"> </span><span class="cx"> # </span><span class="cx"> # application-level model objects </span><span class="lines">@@ -130,6 +130,9 @@ </span><span class="cx"> </span><span class="cx"> </span><span class="cx"> def test_create(self): </span><ins>+ global_connect('sqlite:///', echo=False) + activemapper.create_tables() + </ins><span class="cx"> p1 = self.create_person_one() </span><span class="cx"> </span><span class="cx"> objectstore.commit() </span><span class="lines">@@ -222,9 +225,4 @@ </span><span class="cx"> </span><span class="cx"> </span><span class="cx"> if __name__ == '__main__': </span><del>- # go ahead and setup the database connection, and create the tables - activemapper.engine.connect('sqlite:///', echo=False) - activemapper.create_tables() - - # launch the unit tests </del><span class="cx"> unittest.main() </span><span class="cx">\ No newline at end of file </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-23 21:05: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>[1324] sqlalchemy/branches/schema/test: objectstore mod->session</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1324</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-23 16:05:13 -0500 (Sun, 23 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>objectstore mod->session "objectstore" is now a real object "Objectstore" (will probably change that..)</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemy__init__py">sqlalchemy/branches/schema/lib/sqlalchemy/__init__.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="#sqlalchemybranchesschemalibsqlalchemyormpropertiespy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormquerypy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py</a></li> <li><a href="#sqlalchemybranchesschematestmapperpy">sqlalchemy/branches/schema/test/mapper.py</a></li> <li><a href="#sqlalchemybranchesschematestobjectstorepy">sqlalchemy/branches/schema/test/objectstore.py</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormsessionpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py</a></li> </ul> <h3>Removed Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormobjectstorepy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/objectstore.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemy__init__py"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/__init__.py (1323 => 1324)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/__init__.py 2006-04-23 20:49:51 UTC (rev 1323) +++ sqlalchemy/branches/schema/lib/sqlalchemy/__init__.py 2006-04-23 21:05:13 UTC (rev 1324) </span><span class="lines">@@ -14,10 +14,10 @@ </span><span class="cx"> from sqlalchemy.orm import * </span><span class="cx"> import sqlalchemy.ext.proxy </span><span class="cx"> </span><del>-from sqlalchemy.orm.objectstore import Session, get_session </del><ins>+from sqlalchemy.orm.session import Session, get_session </ins><span class="cx"> </span><span class="cx"> create_engine = sqlalchemy.engine.create_engine </span><del>-create_session = objectstore.Session </del><ins>+create_session = sqlalchemy.orm.session.Session </ins><span class="cx"> </span><span class="cx"> def global_connect(*args, **kwargs): </span><span class="cx"> sqlalchemy.schema.default_metadata.connect(*args, **kwargs) </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymodsthreadlocalpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py (1323 => 1324)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py 2006-04-23 20:49:51 UTC (rev 1323) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py 2006-04-23 21:05:13 UTC (rev 1324) </span><span class="lines">@@ -1,8 +1,8 @@ </span><del>-from sqlalchemy import util, engine -from sqlalchemy.orm import unitofwork, objectstore </del><ins>+from sqlalchemy import util, engine, mapper +from sqlalchemy.orm import unitofwork, session +import sqlalchemy +import sys, types </ins><span class="cx"> </span><del>-import sys - </del><span class="cx"> """this plugin installs thread-local behavior at the Engine and Session level. </span><span class="cx"> </span><span class="cx"> The default Engine strategy will be "threadlocal", producing TLocalEngine instances for create_engine by default. </span><span class="lines">@@ -19,71 +19,102 @@ </span><span class="cx"> explicit Session objects when creating instances and creating queries. </span><span class="cx"> """ </span><span class="cx"> </span><del>-get_session = objectstore.get_session </del><ins>+get_session = session.get_session </ins><span class="cx"> </span><del>-def begin(*obj): - return get_session().begin(*obj) -def commit(*obj): - return get_session().commit(*obj) </del><ins>+class Objectstore(object): + def begin(self, *obj): + return get_session().begin(*obj) + def commit(self, *obj): + return get_session().commit(*obj) + def get_session(self, obj=None): + return get_session(obj=obj) + def flush(self, *obj): + """flushes the current UnitOfWork transaction. if a transaction was begun + via begin(), flushes only those objects that were created, modified, or deleted + since that begin statement. otherwise flushes all objects that have been + changed. </ins><span class="cx"> </span><del>-def flush(*obj): - """flushes the current UnitOfWork transaction. if a transaction was begun - via begin(), flushes only those objects that were created, modified, or deleted - since that begin statement. otherwise flushes all objects that have been - changed. </del><ins>+ if individual objects are submitted, then only those objects are committed, and the + begin/commit cycle is not affected.""" + get_session().flush(*obj) </ins><span class="cx"> </span><del>- if individual objects are submitted, then only those objects are committed, and the - begin/commit cycle is not affected.""" - get_session().flush(*obj) </del><ins>+ def clear(self): + """removes all current UnitOfWorks and IdentityMaps for this thread and + establishes a new one. It is probably a good idea to discard all + current mapped object instances, as they are no longer in the Identity Map.""" + get_session().clear() </ins><span class="cx"> </span><del>-def clear(): - """removes all current UnitOfWorks and IdentityMaps for this thread and - establishes a new one. It is probably a good idea to discard all - current mapped object instances, as they are no longer in the Identity Map.""" - get_session().clear() </del><ins>+ def refresh(self, *obj): + """reloads the state of this object from the database, and cancels any in-memory + changes.""" + get_session().refresh(*obj) </ins><span class="cx"> </span><del>-def refresh(*obj): - """reloads the state of this object from the database, and cancels any in-memory - changes.""" - get_session().refresh(*obj) </del><ins>+ def expire(self, *obj): + """invalidates the data in the given objects and sets them to refresh themselves + the next time they are requested.""" + get_session().expire(*obj) </ins><span class="cx"> </span><del>-def expire(*obj): - """invalidates the data in the given objects and sets them to refresh themselves - the next time they are requested.""" - get_session().expire(*obj) </del><ins>+ def expunge(self, *obj): + get_session().expunge(*obj) </ins><span class="cx"> </span><del>-def expunge(*obj): - get_session().expunge(*obj) </del><ins>+ def delete(self, *obj): + """registers the given objects as to be deleted upon the next commit""" + s = get_session().delete(*obj) </ins><span class="cx"> </span><del>-def delete(*obj): - """registers the given objects as to be deleted upon the next commit""" - s = get_session().delete(*obj) </del><ins>+ def has_key(self, key): + """returns True if the current thread-local IdentityMap contains the given instance key""" + return get_session().has_key(key) </ins><span class="cx"> </span><del>-def has_key(key): - """returns True if the current thread-local IdentityMap contains the given instance key""" - return get_session().has_key(key) </del><ins>+ def has_instance(self, instance): + """returns True if the current thread-local IdentityMap contains the given instance""" + return get_session().has_instance(instance) </ins><span class="cx"> </span><del>-def has_instance(instance): - """returns True if the current thread-local IdentityMap contains the given instance""" - return get_session().has_instance(instance) </del><ins>+ def is_dirty(self, obj): + """returns True if the given object is in the current UnitOfWork's new or dirty list, + or if its a modified list attribute on an object.""" + return get_session().is_dirty(obj) </ins><span class="cx"> </span><del>-def is_dirty(obj): - """returns True if the given object is in the current UnitOfWork's new or dirty list, - or if its a modified list attribute on an object.""" - return get_session().is_dirty(obj) </del><ins>+ def instance_key(self, instance): + """returns the IdentityMap key for the given instance""" + return get_session().instance_key(instance) </ins><span class="cx"> </span><del>-def instance_key(instance): - """returns the IdentityMap key for the given instance""" - return get_session().instance_key(instance) </del><ins>+ def import_instance(self, instance): + return get_session().import_instance(instance) </ins><span class="cx"> </span><del>-def import_instance(instance): - return get_session().import_instance(instance) - </del><ins>+def assign_mapper(class_, *args, **params): + params.setdefault("is_primary", True) + if not isinstance(getattr(class_, '__init__'), types.MethodType): + def __init__(self, **kwargs): + for key, value in kwargs.items(): + setattr(self, key, value) + class_.__init__ = __init__ + m = mapper(class_, *args, **params) + class_.mapper = m + class_.get = m.get + class_.select = m.select + class_.select_by = m.select_by + class_.selectone = m.selectone + class_.get_by = m.get_by + def commit(self): + sqlalchemy.objectstore.commit(self) + def delete(self): + sqlalchemy.objectstore.delete(self) + def expire(self): + sqlalchemy.objectstore.expire(self) + def refresh(self): + sqlalchemy.objectstore.refresh(self) + def expunge(self): + sqlalchemy.objectstore.expunge(self) + class_.commit = commit + class_.delete = delete + class_.expire = expire + class_.refresh = refresh + class_.expunge = expunge </ins><span class="cx"> def install_plugin(): </span><del>- mod = sys.modules[__name__] - for name in ['import_instance', 'instance_key', 'has_instance', 'is_dirty', 'has_key', 'delete', 'expunge', 'expire', 'refresh', 'clear', 'flush', 'begin', 'commit']: - setattr(objectstore, name, getattr(mod, name)) - reg = util.ScopedRegistry(objectstore.Session) - objectstore._default_session = lambda *args, **kwargs: reg() </del><ins>+ reg = util.ScopedRegistry(session.Session) + session._default_session = lambda *args, **kwargs: reg() </ins><span class="cx"> engine.default_strategy = 'threadlocal' </span><ins>+ sqlalchemy.objectstore = Objectstore() + sqlalchemy.assign_mapper = assign_mapper + </ins><span class="cx"> install_plugin() </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyorm__init__py"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py (1323 => 1324)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py 2006-04-23 20:49:51 UTC (rev 1323) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/__init__.py 2006-04-23 21:05:13 UTC (rev 1324) </span><span class="lines">@@ -12,7 +12,7 @@ </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 objectstore </del><ins>+import session </ins><span class="cx"> from exceptions import * </span><span class="cx"> import types as types </span><span class="cx"> from mapper import * </span><span class="lines">@@ -20,8 +20,8 @@ </span><span class="cx"> import mapper as mapperlib </span><span class="cx"> </span><span class="cx"> __all__ = ['relation', 'backref', 'eagerload', 'lazyload', 'noload', 'deferred', 'defer', 'undefer', </span><del>- 'mapper', 'clear_mappers', 'objectstore', 'sql', 'extension', 'class_mapper', 'object_mapper', 'MapperExtension', - 'assign_mapper', 'cascade_mappers' </del><ins>+ 'mapper', 'clear_mappers', 'sql', 'extension', 'class_mapper', 'object_mapper', 'MapperExtension', + 'cascade_mappers' </ins><span class="cx"> ] </span><span class="cx"> </span><span class="cx"> def relation(*args, **kwargs): </span><span class="lines">@@ -95,35 +95,6 @@ </span><span class="cx"> </span><span class="cx"> </span><span class="cx"> </span><del>-def assign_mapper(class_, *args, **params): - params.setdefault("is_primary", True) - if not isinstance(getattr(class_, '__init__'), types.MethodType): - def __init__(self, **kwargs): - for key, value in kwargs.items(): - setattr(self, key, value) - class_.__init__ = __init__ - m = mapper(class_, *args, **params) - class_.mapper = m - class_.get = m.get - class_.select = m.select - class_.select_by = m.select_by - class_.selectone = m.selectone - class_.get_by = m.get_by - def commit(self): - objectstore.commit(self) - def delete(self): - objectstore.delete(self) - def expire(self): - objectstore.expire(self) - def refresh(self): - objectstore.refresh(self) - def expunge(self): - objectstore.expunge(self) - class_.commit = commit - class_.delete = delete - class_.expire = expire - class_.refresh = refresh - class_.expunge = expunge </del><span class="cx"> </span><span class="cx"> def cascade_mappers(*classes_or_mappers): </span><span class="cx"> """given a list of classes and/or mappers, identifies the foreign key relationships </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py (1323 => 1324)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-04-23 20:49:51 UTC (rev 1323) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-04-23 21:05:13 UTC (rev 1324) </span><span class="lines">@@ -12,7 +12,7 @@ </span><span class="cx"> import sync </span><span class="cx"> from sqlalchemy.exceptions import * </span><span class="cx"> import query </span><del>-import objectstore </del><ins>+import session as sessionlib </ins><span class="cx"> import sys </span><span class="cx"> import weakref </span><span class="cx"> import sets </span><span class="lines">@@ -192,7 +192,7 @@ </span><span class="cx"> proplist.append(prop) </span><span class="cx"> </span><span class="cx"> if not mapper_registry.has_key(self.class_key) or self.is_primary or (inherits is not None and inherits._is_primary_mapper()): </span><del>- objectstore.global_attributes.reset_class_managed(self.class_) </del><ins>+ sessionlib.global_attributes.reset_class_managed(self.class_) </ins><span class="cx"> self._init_class() </span><span class="cx"> elif not non_primary: </span><span class="cx"> raise ArgumentError("Class '%s' already has a primary mapper defined. Use is_primary=True to assign a new primary mapper to the class, or use non_primary=True to create a non primary Mapper" % self.class_) </span><span class="lines">@@ -225,7 +225,7 @@ </span><span class="cx"> query = property(_get_query, doc=\ </span><span class="cx"> """returns an instance of sqlalchemy.orm.query.Query, which implements all the query-constructing </span><span class="cx"> methods such as get(), select(), select_by(), etc. The default Query object uses the global thread-local </span><del>- Session from the objectstore package. To get a Query object for a specific Session, call the </del><ins>+ Session from the session package. To get a Query object for a specific Session, call the </ins><span class="cx"> using(session) method.""") </span><span class="cx"> </span><span class="cx"> def get(self, *ident, **kwargs): </span><span class="lines">@@ -348,13 +348,13 @@ </span><span class="cx"> </span><span class="cx"> # this gets the AttributeManager to do some pre-initialization, </span><span class="cx"> # in order to save on KeyErrors later on </span><del>- objectstore.global_attributes.init_attr(self) </del><ins>+ sessionlib.global_attributes.init_attr(self) </ins><span class="cx"> </span><span class="cx"> nohist = kwargs.pop('_mapper_nohistory', False) </span><span class="cx"> if kwargs.has_key('_sa_session'): </span><span class="cx"> session = kwargs.pop('_sa_session') </span><span class="cx"> else: </span><del>- session = objectstore.get_session(self, raiseerror=False) </del><ins>+ session = sessionlib.get_session(self, raiseerror=False) </ins><span class="cx"> if session is not None: </span><span class="cx"> if not nohist: </span><span class="cx"> # register new with the correct session, before the object's </span><span class="lines">@@ -415,11 +415,11 @@ </span><span class="cx"> return result </span><span class="cx"> </span><span class="cx"> def identity_key(self, *primary_key): </span><del>- """returns the instance key for the given identity value. this is a global tracking object used by the objectstore, and is usually available off a mapped object as instance._instance_key.""" - return objectstore.get_id_key(tuple(primary_key), self.class_, self.entity_name) </del><ins>+ """returns the instance key for the given identity value. this is a global tracking object used by the Session, and is usually available off a mapped object as instance._instance_key.""" + return sessionlib.get_id_key(tuple(primary_key), self.class_, self.entity_name) </ins><span class="cx"> </span><span class="cx"> def instance_key(self, instance): </span><del>- """returns the instance key for the given instance. this is a global tracking object used by the objectstore, and is usually available off a mapped object as instance._instance_key.""" </del><ins>+ """returns the instance key for the given instance. this is a global tracking object used by the Session, and is usually available off a mapped object as instance._instance_key.""" </ins><span class="cx"> return self.identity_key(*self.identity(instance)) </span><span class="cx"> </span><span class="cx"> def identity(self, instance): </span><span class="lines">@@ -683,7 +683,7 @@ </span><span class="cx"> return False </span><span class="cx"> </span><span class="cx"> def register_dependencies(self, uowcommit, *args, **kwargs): </span><del>- """called by an instance of objectstore.UOWTransaction to register </del><ins>+ """called by an instance of unitofwork.UOWTransaction to register </ins><span class="cx"> which mappers are dependent on which, as well as DependencyProcessor </span><span class="cx"> objects which will process lists of objects in between saves and deletes.""" </span><span class="cx"> for prop in self.props.values(): </span><span class="lines">@@ -702,7 +702,7 @@ </span><span class="cx"> yield c </span><span class="cx"> </span><span class="cx"> def _identity_key(self, row): </span><del>- return objectstore.get_row_key(row, self.class_, self.pks_by_table[self.table], self.entity_name) </del><ins>+ return sessionlib.get_row_key(row, self.class_, self.pks_by_table[self.table], self.entity_name) </ins><span class="cx"> </span><span class="cx"> def _instance(self, session, row, imap, result = None, populate_existing = False): </span><span class="cx"> """pulls an object instance from the given row and appends it to the given result </span><span class="lines">@@ -767,7 +767,7 @@ </span><span class="cx"> </span><span class="cx"> # this gets the AttributeManager to do some pre-initialization, </span><span class="cx"> # in order to save on KeyErrors later on </span><del>- objectstore.global_attributes.init_attr(obj) </del><ins>+ sessionlib.global_attributes.init_attr(obj) </ins><span class="cx"> </span><span class="cx"> session._bind_to(obj) </span><span class="cx"> return obj </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormobjectstorepy"></a> <div class="delfile"><h4>Deleted: sqlalchemy/branches/schema/lib/sqlalchemy/orm/objectstore.py (1323 => 1324)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/objectstore.py 2006-04-23 20:49:51 UTC (rev 1323) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/objectstore.py 2006-04-23 21:05:13 UTC (rev 1324) </span><span class="lines">@@ -1,448 +0,0 @@ </span><del>-# objectstore.py -# Copyright (C) 2005,2006 Michael Bayer mi...@zz... -# -# This module is part of SQLAlchemy and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - -from sqlalchemy import util -from sqlalchemy.exceptions import * -import unitofwork, query -import weakref -import sqlalchemy -import sqlalchemy.sql as sql - - -class SessionTransaction(object): - def __init__(self, session, parent=None, autoflush=True): - self.session = session - self.connections = {} - self.parent = parent - self.autoflush = autoflush - def connection(self, mapper): - if self.parent is not None: - return self.parent.connection(mapper) - engine = self.session.get_bind(mapper) - return self.add(engine) - def _begin(self): - return SessionTransaction(self.session, self) - def add(self, connection_or_engine): - if self.connections.has_key(connection_or_engine): - return self.connections[connection_or_engine][0] - c = connection_or_engine.contextual_connect() - e = c.engine - if not self.connections.has_key(e): - self.connections[e] = (c, c.begin()) - return self.connections[e][0] - def commit(self): - if self.parent is not None: - return - if self.autoflush: - self.session.flush() - for t in self.connections.values(): - t[1].commit() - self.close() - def rollback(self): - if self.parent is not None: - self.parent.rollback() - return - for k, t in self.connections.iteritems(): - t[1].rollback() - self.close() - def close(self): - if self.parent is not None: - return - for t in self.connections.values(): - t[0].close() - self.session.transaction = None - -class Session(object): - def __init__(self, bind_to=None, hash_key=None, new_imap=True, import_session=None): - if import_session is not None: - self.uow = unitofwork.UnitOfWork(identity_map=import_session.uow.identity_map) - elif new_imap is False: - self.uow = unitofwork.UnitOfWork(identity_map=objectstore.get_session().uow.identity_map) - else: - self.uow = unitofwork.UnitOfWork() - - self.bind_to = bind_to - self.binds = {} - self.transaction = None - if hash_key is None: - self.hash_key = id(self) - else: - self.hash_key = hash_key - _sessions[self.hash_key] = self - - def create_transaction(self, **kwargs): - """returns a new SessionTransaction corresponding to an existing or new transaction. - if the transaction is new, the returned SessionTransaction will have commit control - over the underlying transaction, else will have rollback control only.""" - if self.transaction is not None: - return self.transaction._begin() - else: - self.transaction = SessionTransaction(self, **kwargs) - return self.transaction - def connect(self, mapper=None, **kwargs): - """returns a unique connection corresponding to the given mapper. this connection - will not be part of any pre-existing transactional context.""" - return self.get_bind(mapper).connect(**kwargs) - def connection(self, mapper, **kwargs): - """returns a Connection corresponding to the given mapper. used by the execute() - method which performs select operations for Mapper and Query. - if this Session is transactional, - the connection will be in the context of this session's transaction. otherwise, the connection - is returned by the contextual_connect method, which some Engines override to return a thread-local - connection, and will have close_with_result set to True. - - the given **kwargs will be sent to the engine's contextual_connect() method, if no transaction is in progress.""" - if self.transaction is not None: - return self.transaction.connection(mapper) - else: - return self.get_bind(mapper).contextual_connect(**kwargs) - def execute(self, mapper, clause, params, **kwargs): - """using the given mapper to identify the appropriate Engine or Connection to be used for statement execution, - executes the given ClauseElement using the provided parameter dictionary. Returns a ResultProxy corresponding - to the execution's results. If this method allocates a new Connection for the operation, then the ResultProxy's close() - method will release the resources of the underlying Connection, otherwise its a no-op. - """ - return self.connection(mapper).execute(clause, params, **kwargs) - def close(self): - """closes this Session. - - TODO: what should we do here ? - """ - if self.transaction is not None: - self.transaction.close() - def bind_mapper(self, mapper, bindto): - """binds the given Mapper to the given Engine or Connection. All subsequent operations involving this - Mapper will use the given bindto.""" - self.binds[mapper] = bindto - def bind_table(self, table, bindto): - """binds the given Table to the given Engine or Connection. All subsequent operations involving this - Table will use the given bindto.""" - self.binds[table] = bindto - def get_bind(self, mapper): - """given a Mapper, returns the Engine or Connection which is used to execute statements on behalf of this - Mapper. Calling connect() on the return result will always result in a Connection object. This method - disregards any SessionTransaction that may be in progress. - - The order of searching is as follows: - - if an Engine or Connection was bound to this Mapper specifically within this Session, returns that - Engine or Connection. - - if an Engine or Connection was bound to this Mapper's underlying Table within this Session - (i.e. not to the Table directly), returns that Engine or Conneciton. - - if an Engine or Connection was bound to this Session, returns that Engine or Connection. - - finally, returns the Engine which was bound directly to the Table's MetaData object. - - If no Engine is bound to the Table, an exception is raised. - """ - if mapper is None: - return self.bind_to - elif self.binds.has_key(mapper): - return self.binds[mapper] - elif self.binds.has_key(mapper.table): - return self.binds[mapper.table] - elif self.bind_to is not None: - return self.bind_to - else: - return mapper.table.engine - def query(self, mapper_or_class): - """given a mapper or Class, returns a new Query object corresponding to this Session and the mapper, or the classes' primary mapper.""" - if isinstance(mapper_or_class, type): - return query.Query(class_mapper(mapper_or_class), self) - else: - return query.Query(mapper_or_class, self) - def _sql(self): - class SQLProxy(object): - def __getattr__(self, key): - def call(*args, **kwargs): - kwargs[engine] = self.engine - return getattr(sql, key)(*args, **kwargs) - - sql = property(_sql) - - - def get_id_key(ident, class_, entity_name=None): - """returns an identity-map key for use in storing/retrieving an item from the identity - map, given a tuple of the object's primary key values. - - ident - a tuple of primary key values corresponding to the object to be stored. these - values should be in the same order as the primary keys of the table - - class_ - a reference to the object's class - - entity_name - optional string name to further qualify the class - """ - return (class_, tuple(ident), entity_name) - get_id_key = staticmethod(get_id_key) - - def get_row_key(row, class_, primary_key, entity_name=None): - """returns an identity-map key for use in storing/retrieving an item from the identity - map, given a result set row. - - row - a sqlalchemy.dbengine.RowProxy instance or other map corresponding result-set - column names to their values within a row. - - class_ - a reference to the object's class - - primary_key - a list of column objects that will target the primary key values - in the given row. - - entity_name - optional string name to further qualify the class - """ - return (class_, tuple([row[column] for column in primary_key]), entity_name) - get_row_key = staticmethod(get_row_key) - - def begin(self, *obj): - """deprecated""" - raise InvalidRequestError("Session.begin() is deprecated. use install_mod('legacy_session') to enable the old behavior") - def commit(self, *obj): - """deprecated""" - raise InvalidRequestError("Session.commit() is deprecated. use install_mod('legacy_session') to enable the old behavior") - - def flush(self, *obj): - """flushes all the object modifications present in this session to the database. if object - arguments are given, then only those objects (and immediate dependencies) are flushed.""" - self.uow.flush(self, *obj) - - def load(self, class_, *ident): - """given a class and a primary key identifier, loads the corresponding object.""" - return self.query(class_).get(*ident) - - def refresh(self, *obj): - """reloads the attributes for the given objects from the database, clears - any changes made.""" - for o in obj: - self.uow.refresh(o) - - def expire(self, *obj): - """invalidates the data in the given objects and sets them to refresh themselves - the next time they are requested.""" - for o in obj: - self.uow.expire(o) - - def expunge(self, *obj): - """removes the given objects from this Session. this will free all internal references to the objects.""" - for o in obj: - self.uow.expunge(o) - - def save(self, *obj, **kwargs): - """adds unsaved objects to this Session. - - The 'entity_name' keyword argument can also be given which will be assigned - to the instances if given. - """ - for o in obj: - for c in object_mapper(o, **kwargs).cascade_iterator('save-update', o): - if c is o: - self._save_impl(c, **kwargs) - else: - self.save_or_update(c, **kwargs) - - def update(self, *obj, **kwargs): - for o in obj: - for c in object_mapper(o, **kwargs).cascade_iterator('save-update', o): - if c is o: - self._update_impl(c, **kwargs) - else: - self.save_or_update(c, **kwargs) - - def save_or_update(self, *obj, **kwargs): - for o in obj: - for c in object_mapper(o, *kwargs).cascade_iterator('save-update', o): - key = getattr(o, '_instance_key', None) - if key is None: - self._save_impl(c, **kwargs) - else: - self._update_impl(c, **kwargs) - - def _save_impl(self, object, **kwargs): - if hasattr(object, '_instance_key'): - if not self.uow.has_key(object._instance_key): - raise InvalidRequestError("Instance '%s' attached to a different Session" % repr(object)) - else: - entity_name = kwargs.get('entity_name', None) - if entity_name is not None: - m = class_mapper(object.__class__, entity_name=entity_name) - m._assign_entity_name(object) - self._register_new(object) - - def _update_impl(self, object, **kwargs): - if self._is_bound(object) and object not in self.deleted: - return - if not hasattr(object, '_instance_key'): - raise InvalidRequestError("Instance '%s' is not persisted" % repr(object)) - if global_attributes.is_modified(object): - self._register_dirty(object) - else: - self._register_clean(object) - - def _register_new(self, obj): - self._bind_to(obj) - self.uow.register_new(obj) - def _register_dirty(self, obj): - self._bind_to(obj) - self.uow.register_dirty(obj) - def _register_clean(self, obj): - self._bind_to(obj) - self.uow.register_clean(obj) - def _register_deleted(self, obj): - self._bind_to(obj) - self.uow.register_deleted(obj) - def _bind_to(self, obj): - """given an object, binds it to this session. changes on the object will affect - the currently scoped UnitOfWork maintained by this session.""" - if getattr(obj, '_sa_session_id', None) != self.hash_key: - old = getattr(obj, '_sa_session_id', None) - # remove from old session. we do this gingerly since _sessions is a WeakValueDict - # and it might be affected by other threads - if old is not None: - try: - sess = _sessions[old] - except KeyError: - sess = None - if sess is not None: - sess.expunge(old) - obj._sa_session_id = self.hash_key - def _is_bound(self, obj): - return getattr(obj, '_sa_session_id', None) == self.hash_key - def __contains__(self, obj): - return self._is_bound(obj) and (obj in self.uow.new or self.uow.has_key(obj._instance_key)) - - def _get(self, key): - return self.uow._get(key) - def has_key(self, key): - return self.uow.has_key(key) - def is_expired(self, instance, **kwargs): - return self.uow.is_expired(instance, **kwargs) - - dirty = property(lambda s:s.uow.dirty) - deleted = property(lambda s:s.uow.deleted) - new = property(lambda s:s.uow.new) - identity_map = property(lambda s:s.uow.identity_map) - - def clear(self): - """removes all object instances from this Session. this is equivalent to calling expunge() for all - objects in this Session.""" - self.uow = unitofwork.UnitOfWork() - - def delete(self, *obj, **kwargs): - """registers the given objects to be deleted upon the next flush(). If the given objects are not part of this - Session, they will be imported. the objects are expected to either have an _instance_key - attribute or have all of their primary key attributes populated. - - the keyword argument 'entity_name' can also be provided which will be used by the import.""" - for o in obj: - for c in object_mapper(o, **kwargs).cascade_iterator('delete', o): - if not self._is_bound(c): - c = self.import_(c, **kwargs) - self.uow.register_deleted(c) - - - def merge(self, instance, entity_name=None): - """given an instance that represents a saved item, adds it to this session. - the return value is either the given instance, or if an instance corresponding to the - identity of the given instance already exists within this session, then that instance is returned; - the returned instance should always be used following this method. - - if the given instance does not have an _instance_key and also does not have all - of its primary key attributes populated, an exception is raised. similarly, if no - mapper can be located for the given instance, an exception is raised. - - this method should be used for any object instance that is coming from a serialized - storage, or was loaded by a Session other than this one. - - the keyword parameter entity_name is optional and is used to locate a Mapper for this - class which also specifies the given entity name. - """ - if instance is None: - return None - key = getattr(object, '_instance_key', None) - if key is None: - mapper = object_mapper(object, raiseerror=False) - if mapper is None: - mapper = class_mapper(object, entity_name=entity_name) - ident = mapper.identity(object) - for k in ident: - if k is None: - if raiseerror: - raise InvalidRequestError("Instance '%s' does not have a full set of identity values, and does not represent a saved entity in the database. Use the add() method to add unsaved instances to this Session." % str(object)) - else: - return None - key = mapper.identity_key(*ident) - u = self.uow - if u.identity_map.has_key(key): - return u.identity_map[key] - else: - instance._instance_key = key - u.identity_map[key] = instance - self._bind_to(instance) - return instance - - def import_instance(self, *args, **kwargs): - """deprecated; a synynom for import()""" - return self.merge(*args, **kwargs) - -def get_id_key(ident, class_, entity_name=None): - return Session.get_id_key(ident, class_, entity_name) - -def get_row_key(row, class_, primary_key, entity_name=None): - return Session.get_row_key(row, class_, primary_key, entity_name) - -def mapper(*args, **params): - return sqlalchemy.orm.mapper(*args, **params) - -def object_mapper(obj, **kwargs): - return sqlalchemy.orm.object_mapper(obj, **kwargs) - -def class_mapper(class_, **kwargs): - return sqlalchemy.orm.class_mapper(class_, **kwargs) - -# this is the AttributeManager instance used to provide attribute behavior on objects. -# to all the "global variable police" out there: its a stateless object. -global_attributes = unitofwork.global_attributes - -# this dictionary maps the hash key of a Session to the Session itself, and -# acts as a Registry with which to locate Sessions. this is to enable -# object instances to be associated with Sessions without having to attach the -# actual Session object directly to the object instance. -_sessions = weakref.WeakValueDictionary() - -def get_session(obj=None, raiseerror=True): - """returns the Session corrseponding to the given object instance. By default, if the object is not bound - to any Session, then an error is raised (or None is returned if raiseerror=False). This behavior can be changed - using the "threadlocal" mod, which will add an additional step to return a Session that is bound to the current - thread.""" - if obj is not None: - # does it have a hash key ? - hashkey = getattr(obj, '_sa_session_id', None) - if hashkey is not None: - # ok, return that - try: - return _sessions[hashkey] - except KeyError: - if raiseerror: - raise InvalidRequestError("Session '%s' referenced by object '%s' no longer exists" % (hashkey, repr(obj))) - else: - return None - - return _default_session(obj=obj, raiseerror=raiseerror) - -def _default_session(obj=None, raiseerror=True): - if obj is None: - if raiseerror: - raise InvalidRequestError("Thread-local Sessions are disabled by default. Use 'import sqlalchemy.mods.threadlocal' to enable.") - else: - return None - else: - if raiseerror: - raise InvalidRequestError("Object '%s' not bound to any Session" % (repr(obj))) - else: - return None - -unitofwork.get_session = get_session - </del></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py (1323 => 1324)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py 2006-04-23 20:49:51 UTC (rev 1323) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py 2006-04-23 21:05:13 UTC (rev 1324) </span><span class="lines">@@ -16,7 +16,7 @@ </span><span class="cx"> import sqlalchemy.attributes as attributes </span><span class="cx"> import sync </span><span class="cx"> import mapper </span><del>-import objectstore </del><ins>+import session as sessionlib </ins><span class="cx"> import dependency </span><span class="cx"> import util as mapperutil </span><span class="cx"> from sqlalchemy.exceptions import * </span><span class="lines">@@ -34,7 +34,7 @@ </span><span class="cx"> def setattr(self, object, value): </span><span class="cx"> setattr(object, self.key, value) </span><span class="cx"> def get_history(self, obj, passive=False): </span><del>- return objectstore.global_attributes.get_history(obj, self.key, passive=passive) </del><ins>+ return sessionlib.global_attributes.get_history(obj, self.key, passive=passive) </ins><span class="cx"> def copy(self): </span><span class="cx"> return ColumnProperty(*self.columns) </span><span class="cx"> def setup(self, key, statement, eagertable=None, **options): </span><span class="lines">@@ -48,7 +48,7 @@ </span><span class="cx"> # establish a SmartProperty property manager on the object for this key </span><span class="cx"> if parent._is_primary_mapper(): </span><span class="cx"> #print "regiser col on class %s key %s" % (parent.class_.__name__, key) </span><del>- objectstore.global_attributes.register_attribute(parent.class_, key, uselist = False) </del><ins>+ sessionlib.global_attributes.register_attribute(parent.class_, key, uselist = False) </ins><span class="cx"> def execute(self, session, instance, row, identitykey, imap, isnew): </span><span class="cx"> if isnew: </span><span class="cx"> #print "POPULATING OBJ", instance.__class__.__name__, "COL", self.columns[0]._label, "WITH DATA", row[self.columns[0]], "ROW IS A", row.__class__.__name__, "COL ID", id(self.columns[0]) </span><span class="lines">@@ -70,12 +70,12 @@ </span><span class="cx"> # establish a SmartProperty property manager on the object for this key, </span><span class="cx"> # containing a callable to load in the attribute </span><span class="cx"> if self.is_primary(): </span><del>- objectstore.global_attributes.register_attribute(parent.class_, key, uselist=False, callable_=lambda i:self.setup_loader(i)) </del><ins>+ sessionlib.global_attributes.register_attribute(parent.class_, key, uselist=False, callable_=lambda i:self.setup_loader(i)) </ins><span class="cx"> def setup_loader(self, instance): </span><span class="cx"> if not self.parent.is_assigned(instance): </span><span class="cx"> return object_mapper(instance).props[self.key].setup_loader(instance) </span><span class="cx"> def lazyload(): </span><del>- session = objectstore.get_session(instance) </del><ins>+ session = sessionlib.get_session(instance) </ins><span class="cx"> connection = session.connection(self.parent) </span><span class="cx"> clause = sql.and_() </span><span class="cx"> try: </span><span class="lines">@@ -96,7 +96,7 @@ </span><span class="cx"> if prop is self: </span><span class="cx"> continue </span><span class="cx"> instance.__dict__[prop.key] = row[prop.columns[0]] </span><del>- objectstore.global_attributes.create_history(instance, prop.key, uselist=False) </del><ins>+ sessionlib.global_attributes.create_history(instance, prop.key, uselist=False) </ins><span class="cx"> return row[self.columns[0]] </span><span class="cx"> else: </span><span class="cx"> return connection.scalar(sql.select([self.columns[0]], clause, use_labels=True),None) </span><span class="lines">@@ -108,9 +108,9 @@ </span><span class="cx"> def execute(self, session, instance, row, identitykey, imap, isnew): </span><span class="cx"> if isnew: </span><span class="cx"> if not self.is_primary(): </span><del>- objectstore.global_attributes.create_history(instance, self.key, False, callable_=self.setup_loader(instance)) </del><ins>+ sessionlib.global_attributes.create_history(instance, self.key, False, callable_=self.setup_loader(instance)) </ins><span class="cx"> else: </span><del>- objectstore.global_attributes.reset_history(instance, self.key) </del><ins>+ sessionlib.global_attributes.reset_history(instance, self.key) </ins><span class="cx"> </span><span class="cx"> mapper.ColumnProperty = ColumnProperty </span><span class="cx"> </span><span class="lines">@@ -166,9 +166,9 @@ </span><span class="cx"> recursive = sets.Set() </span><span class="cx"> </span><span class="cx"> if self.uselist: </span><del>- childlist = objectstore.global_attributes.get_history(object, self.key, passive = False) </del><ins>+ childlist = sessionlib.global_attributes.get_history(object, self.key, passive = False) </ins><span class="cx"> else: </span><del>- childlist = objectstore.global_attributes.get_history(object, self.key) </del><ins>+ childlist = sessionlib.global_attributes.get_history(object, self.key) </ins><span class="cx"> </span><span class="cx"> for c in childlist.added_items() + childlist.deleted_items() + childlist.unchanged_items(): </span><span class="cx"> if c is not None: </span><span class="lines">@@ -247,14 +247,14 @@ </span><span class="cx"> </span><span class="cx"> if self.backref is not None: </span><span class="cx"> self.backref.compile(self) </span><del>- elif not objectstore.global_attributes.is_class_managed(parent.class_, key): </del><ins>+ elif not sessionlib.global_attributes.is_class_managed(parent.class_, key): </ins><span class="cx"> raise ArgumentError("Attempting to assign a new relation '%s' to a non-primary mapper on class '%s'. New relations can only be added to the primary mapper, i.e. the very first mapper created for class '%s' " % (key, parent.class_.__name__, parent.class_.__name__)) </span><span class="cx"> </span><span class="cx"> self.do_init_subclass(key, parent) </span><span class="cx"> </span><span class="cx"> def _set_class_attribute(self, class_, key): </span><span class="cx"> """sets attribute behavior on our target class.""" </span><del>- objectstore.global_attributes.register_attribute(class_, key, uselist = self.uselist, extension=self.attributeext, cascade=self.cascade, trackparent=True) </del><ins>+ sessionlib.global_attributes.register_attribute(class_, key, uselist = self.uselist, extension=self.attributeext, cascade=self.cascade, trackparent=True) </ins><span class="cx"> </span><span class="cx"> def _get_direction(self): </span><span class="cx"> """determines our 'direction', i.e. do we represent one to many, many to many, etc.""" </span><span class="lines">@@ -334,7 +334,7 @@ </span><span class="cx"> if self.is_primary(): </span><span class="cx"> return </span><span class="cx"> #print "PLAIN PROPLOADER EXEC NON-PRIAMRY", repr(id(self)), repr(self.mapper.class_), self.key </span><del>- objectstore.global_attributes.create_history(instance, self.key, self.uselist, cascade=self.cascade, trackparent=True) </del><ins>+ sessionlib.global_attributes.create_history(instance, self.key, self.uselist, cascade=self.cascade, trackparent=True) </ins><span class="cx"> </span><span class="cx"> def register_dependencies(self, uowcommit): </span><span class="cx"> self._dependency_processor.register_dependencies(uowcommit) </span><span class="lines">@@ -367,7 +367,7 @@ </span><span class="cx"> def _set_class_attribute(self, class_, key): </span><span class="cx"> # establish a class-level lazy loader on our class </span><span class="cx"> #print "SETCLASSATTR LAZY", repr(class_), key </span><del>- objectstore.global_attributes.register_attribute(class_, key, uselist = self.uselist, callable_=lambda i: self.setup_loader(i), extension=self.attributeext, cascade=self.cascade, trackparent=True) </del><ins>+ sessionlib.global_attributes.register_attribute(class_, key, uselist = self.uselist, callable_=lambda i: self.setup_loader(i), extension=self.attributeext, cascade=self.cascade, trackparent=True) </ins><span class="cx"> </span><span class="cx"> def setup_loader(self, instance): </span><span class="cx"> if not self.parent.is_assigned(instance): </span><span class="lines">@@ -375,7 +375,7 @@ </span><span class="cx"> def lazyload(): </span><span class="cx"> params = {} </span><span class="cx"> allparams = True </span><del>- session = objectstore.get_session(instance) </del><ins>+ session = sessionlib.get_session(instance) </ins><span class="cx"> #print "setting up loader, lazywhere", str(self.lazywhere) </span><span class="cx"> for col, bind in self.lazybinds.iteritems(): </span><span class="cx"> params[bind.key] = self.parent._getattrbycolumn(instance, col) </span><span class="lines">@@ -415,14 +415,14 @@ </span><span class="cx"> #print "EXEC NON-PRIAMRY", repr(self.mapper.class_), self.key </span><span class="cx"> # we are not the primary manager for this attribute on this class - set up a per-instance lazyloader, </span><span class="cx"> # which will override the class-level behavior </span><del>- objectstore.global_attributes.create_history(instance, self.key, self.uselist, callable_=self.setup_loader(instance), cascade=self.cascade, trackparent=True) </del><ins>+ sessionlib.global_attributes.create_history(instance, self.key, self.uselist, callable_=self.setup_loader(instance), cascade=self.cascade, trackparent=True) </ins><span class="cx"> else: </span><span class="cx"> #print "EXEC PRIMARY", repr(self.mapper.class_), self.key </span><span class="cx"> # we are the primary manager for this attribute on this class - reset its per-instance attribute state, </span><span class="cx"> # so that the class-level lazy loader is executed when next referenced on this instance. </span><span class="cx"> # this usually is not needed unless the constructor of the object referenced the attribute before we got </span><span class="cx"> # to load data into it. </span><del>- objectstore.global_attributes.reset_history(instance, self.key) </del><ins>+ sessionlib.global_attributes.reset_history(instance, self.key) </ins><span class="cx"> </span><span class="cx"> def create_lazy_clause(table, primaryjoin, secondaryjoin, foreignkey): </span><span class="cx"> binds = {} </span><span class="lines">@@ -566,7 +566,7 @@ </span><span class="cx"> if isnew: </span><span class="cx"> # new row loaded from the database. initialize a blank container on the instance. </span><span class="cx"> # this will override any per-class lazyloading type of stuff. </span><del>- h = objectstore.global_attributes.create_history(instance, self.key, self.uselist, cascade=self.cascade, trackparent=True) </del><ins>+ h = sessionlib.global_attributes.create_history(instance, self.key, self.uselist, cascade=self.cascade, trackparent=True) </ins><span class="cx"> </span><span class="cx"> if not self.uselist: </span><span class="cx"> if isnew: </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormquerypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py (1323 => 1324)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py 2006-04-23 20:49:51 UTC (rev 1323) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py 2006-04-23 21:05:13 UTC (rev 1324) </span><span class="lines">@@ -5,7 +5,7 @@ </span><span class="cx"> # the MIT License: http://www.opensource.org/licenses/mit-license.php </span><span class="cx"> </span><span class="cx"> </span><del>-import objectstore </del><ins>+import session as sessionlib </ins><span class="cx"> import sqlalchemy.sql as sql </span><span class="cx"> import sqlalchemy.util as util </span><span class="cx"> from sqlalchemy.exceptions import * </span><span class="lines">@@ -28,7 +28,7 @@ </span><span class="cx"> self._get_clause = self.mapper._get_clause </span><span class="cx"> def _get_session(self): </span><span class="cx"> if self._session is None: </span><del>- return objectstore.get_session() </del><ins>+ return sessionlib.get_session() </ins><span class="cx"> else: </span><span class="cx"> return self._session </span><span class="cx"> table = property(lambda s:s.mapper.table) </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormsessionpyfromrev1323sqlalchemybranchesschemalibsqlalchemyormobjectstorepy"></a> <div class="copfile"><h4>Copied: sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py (from rev 1323, sqlalchemy/branches/schema/lib/sqlalchemy/orm/objectstore.py) ( => )</h4> <pre class="diff"><span> <span class="info">Modified: sqlalchemy/branches/schema/test/mapper.py =================================================================== </span><del>--- sqlalchemy/branches/schema/test/mapper.py 2006-04-23 20:49:51 UTC (rev 1323) </del><ins>+++ sqlalchemy/branches/schema/test/mapper.py 2006-04-23 21:05:13 UTC (rev 1324) </ins><span class="lines">@@ -125,7 +125,7 @@ </span><span class="cx"> self.assert_sql_count(db, go, 1) </span><span class="cx"> </span><span class="cx"> def testsessionpropigation(self): </span><del>- sess = objectstore.Session() </del><ins>+ sess = create_session() </ins><span class="cx"> m = mapper(User, users, properties={'addresses':relation(mapper(Address, addresses), lazy=True)}) </span><span class="cx"> u = m.using(sess).get(7) </span><span class="cx"> assert objectstore.get_session(u) is sess </span><span class="lines">@@ -708,7 +708,7 @@ </span><span class="cx"> </span><span class="cx"> def testcompile(self): </span><span class="cx"> """tests deferred operation of a pre-compiled mapper statement""" </span><del>- session = objectstore.Session() </del><ins>+ session = create_session() </ins><span class="cx"> m = mapper(User, users, properties = dict( </span><span class="cx"> addresses = relation(mapper(Address, addresses), lazy = False) </span... [truncated message content] |
From: <co...@sq...> - 2006-04-23 20:44:13
|
<!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>[1322] sqlalchemy/branches/schema/test: instances are created using __new__ instead of __init__</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1322</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-23 15:43:59 -0500 (Sun, 23 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>instances are created using __new__ instead of __init__</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py</a></li> <li><a href="#sqlalchemybranchesschematestmapperpy">sqlalchemy/branches/schema/test/mapper.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemymappingmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py (1321 => 1322)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py 2006-04-23 20:37:53 UTC (rev 1321) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py 2006-04-23 20:43:59 UTC (rev 1322) </span><span class="lines">@@ -724,7 +724,7 @@ </span><span class="cx"> imap[identitykey] = instance </span><span class="cx"> for prop in self.props.values(): </span><span class="cx"> prop.execute(session, instance, row, identitykey, imap, True) </span><del>- if self.extension.append_result(self, row, imap, result, instance, isnew, populate_existing=populate_existing) is EXT_PASS: </del><ins>+ if self.extension.append_result(self, session, row, imap, result, instance, isnew, populate_existing=populate_existing) is EXT_PASS: </ins><span class="cx"> if result is not None: </span><span class="cx"> result.append_nohistory(instance) </span><span class="cx"> return instance </span><span class="lines">@@ -738,9 +738,9 @@ </span><span class="cx"> if row[col] is None: </span><span class="cx"> return None </span><span class="cx"> # plugin point </span><del>- instance = self.extension.create_instance(self, row, imap, self.class_) </del><ins>+ instance = self.extension.create_instance(self, session, row, imap, self.class_) </ins><span class="cx"> if instance is EXT_PASS: </span><del>- instance = self.class_(_mapper_nohistory=True, _sa_entity_name=self.entity_name, _sa_session=session) </del><ins>+ instance = self._create_instance(session) </ins><span class="cx"> imap[identitykey] = instance </span><span class="cx"> isnew = True </span><span class="cx"> else: </span><span class="lines">@@ -753,11 +753,25 @@ </span><span class="cx"> # instances from the row and possibly populate this item. </span><span class="cx"> if self.extension.populate_instance(self, session, instance, row, identitykey, imap, isnew) is EXT_PASS: </span><span class="cx"> self.populate_instance(session, instance, row, identitykey, imap, isnew) </span><del>- if self.extension.append_result(self, row, imap, result, instance, isnew, populate_existing=populate_existing) is EXT_PASS: </del><ins>+ if self.extension.append_result(self, session, row, imap, result, instance, isnew, populate_existing=populate_existing) is EXT_PASS: </ins><span class="cx"> if result is not None: </span><span class="cx"> result.append_nohistory(instance) </span><span class="cx"> return instance </span><span class="cx"> </span><ins>+ def _create_instance(self, session): + + #return self.class_(_mapper_nohistory=True, _sa_entity_name=self.entity_name, _sa_session=session) + + obj = self.class_.__new__(self.class_) + obj._entity_name = self.entity_name + + # this gets the AttributeManager to do some pre-initialization, + # in order to save on KeyErrors later on + objectstore.global_attributes.init_attr(obj) + + session._bind_to(obj) + return obj + </ins><span class="cx"> def translate_row(self, tomapper, row): </span><span class="cx"> """attempts to take a row and translate its values to a row that can </span><span class="cx"> be understood by another mapper. breaks the column references down to their </span><span class="lines">@@ -857,7 +871,7 @@ </span><span class="cx"> return EXT_PASS </span><span class="cx"> else: </span><span class="cx"> return self.next.select(query, *args, **kwargs) </span><del>- def create_instance(self, mapper, row, imap, class_): </del><ins>+ def create_instance(self, mapper, session, row, imap, class_): </ins><span class="cx"> """called when a new object instance is about to be created from a row. </span><span class="cx"> the method can choose to create the instance itself, or it can return </span><span class="cx"> None to indicate normal object creation should take place. </span><span class="lines">@@ -874,8 +888,8 @@ </span><span class="cx"> if self.next is None: </span><span class="cx"> return EXT_PASS </span><span class="cx"> else: </span><del>- return self.next.create_instance(mapper, row, imap, class_) - def append_result(self, mapper, row, imap, result, instance, isnew, populate_existing=False): </del><ins>+ return self.next.create_instance(mapper, session, row, imap, class_) + def append_result(self, mapper, session, row, imap, result, instance, isnew, populate_existing=False): </ins><span class="cx"> """called when an object instance is being appended to a result list. </span><span class="cx"> </span><span class="cx"> If this method returns True, it is assumed that the mapper should do the appending, else </span><span class="lines">@@ -904,7 +918,7 @@ </span><span class="cx"> if self.next is None: </span><span class="cx"> return EXT_PASS </span><span class="cx"> else: </span><del>- return self.next.append_result(mapper, row, imap, result, instance, isnew, populate_existing) </del><ins>+ return self.next.append_result(mapper, session, row, imap, result, instance, isnew, populate_existing) </ins><span class="cx"> def populate_instance(self, mapper, session, instance, row, identitykey, imap, isnew): </span><span class="cx"> """called right before the mapper, after creating an instance from a row, passes the row </span><span class="cx"> to its MapperProperty objects which are responsible for populating the object's attributes. </span></span></pre></div> <a id="sqlalchemybranchesschematestmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/mapper.py (1321 => 1322)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/mapper.py 2006-04-23 20:37:53 UTC (rev 1321) +++ sqlalchemy/branches/schema/test/mapper.py 2006-04-23 20:43:59 UTC (rev 1322) </span><span class="lines">@@ -550,7 +550,7 @@ </span><span class="cx"> </span><span class="cx"> def testbackwardsonetoone(self): </span><span class="cx"> m = mapper(Address, addresses, properties = dict( </span><del>- user = relation(mapper(User, users, properties = {'id':users.c.user_id}), lazy = True) </del><ins>+ user = relation(mapper(User, users), lazy = True) </ins><span class="cx"> )) </span><span class="cx"> l = m.select(addresses.c.address_id == 1) </span><span class="cx"> self.echo(repr(l)) </span></span></pre> </div> </div> </body> </html> |
<!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>[1321] sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py: added temporary option "construct_new" to mapper which will cause the mapper to use __new__ to create loaded instances instead of the __init__ method</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1321</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-23 15:37:53 -0500 (Sun, 23 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>added temporary option "construct_new" to mapper which will cause the mapper to use __new__ to create loaded instances instead of the __init__ method</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemymappingmapperpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemymappingmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py (1320 => 1321)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-04-22 22:15:36 UTC (rev 1320) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-04-23 20:37:53 UTC (rev 1321) </span><span class="lines">@@ -49,6 +49,7 @@ </span><span class="cx"> entity_name = None, </span><span class="cx"> always_refresh = False, </span><span class="cx"> version_id_col = None, </span><ins>+ construct_new = False, </ins><span class="cx"> **kwargs): </span><span class="cx"> </span><span class="cx"> if primarytable is not None: </span><span class="lines">@@ -73,6 +74,7 @@ </span><span class="cx"> self._options = {} </span><span class="cx"> self.always_refresh = always_refresh </span><span class="cx"> self.version_id_col = version_id_col </span><ins>+ self.construct_new = construct_new </ins><span class="cx"> </span><span class="cx"> if not issubclass(class_, object): </span><span class="cx"> raise ArgumentError("Class '%s' is not a new-style class" % class_.__name__) </span><span class="lines">@@ -724,7 +726,7 @@ </span><span class="cx"> # plugin point </span><span class="cx"> instance = self.extension.create_instance(self, row, imap, self.class_) </span><span class="cx"> if instance is EXT_PASS: </span><del>- instance = self.class_(_mapper_nohistory=True, _sa_entity_name=self.entity_name, _sa_session=session) </del><ins>+ instance = self._create_instance(session) </ins><span class="cx"> imap[identitykey] = instance </span><span class="cx"> isnew = True </span><span class="cx"> else: </span><span class="lines">@@ -742,6 +744,21 @@ </span><span class="cx"> result.append_nohistory(instance) </span><span class="cx"> return instance </span><span class="cx"> </span><ins>+ def _create_instance(self, session): + if not self.construct_new: + return self.class_(_mapper_nohistory=True, _sa_entity_name=self.entity_name, _sa_session=session) + + obj = self.class_.__new__(self.class_) + obj._entity_name = self.entity_name + + # this gets the AttributeManager to do some pre-initialization, + # in order to save on KeyErrors later on + objectstore.global_attributes.init_attr(obj) + + session._bind_to(obj) + + return obj + </ins><span class="cx"> def translate_row(self, tomapper, row): </span><span class="cx"> """attempts to take a row and translate its values to a row that can </span><span class="cx"> be understood by another mapper. breaks the column references down to their </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-22 22:15:47
|
<!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>[1320] sqlalchemy/branches/schema/test/selectresults.py: sel</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1320</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-22 17:15:36 -0500 (Sat, 22 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>sel</pre> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemybranchesschematestselectresultspy">sqlalchemy/branches/schema/test/selectresults.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschematestselectresultspy"></a> <div class="addfile"><h4>Added: sqlalchemy/branches/schema/test/selectresults.py (1319 => 1320)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/selectresults.py 2006-04-22 21:14:43 UTC (rev 1319) +++ sqlalchemy/branches/schema/test/selectresults.py 2006-04-22 22:15:36 UTC (rev 1320) </span><span class="lines">@@ -0,0 +1,68 @@ </span><ins>+from testbase import PersistTest +import testbase + +from sqlalchemy import * + +from sqlalchemy.ext.selectresults import SelectResultsExt + +class Foo(object): + pass + +class SelectResultsTest(PersistTest): + def setUpAll(self): + global foo + foo = Table('foo', testbase.db, + Column('id', Integer, Sequence('foo_id_seq'), primary_key=True), + Column('bar', Integer)) + + assign_mapper(Foo, foo, extension=SelectResultsExt()) + foo.create() + for i in range(100): + Foo(bar=i) + objectstore.flush() + + def setUp(self): + self.orig = Foo.mapper.select_whereclause() + self.res = Foo.select() + + def tearDownAll(self): + global foo + foo.drop() + + def test_slice(self): + assert self.res[1] == self.orig[1] + assert list(self.res[10:20]) == self.orig[10:20] + assert list(self.res[10:]) == self.orig[10:] + assert list(self.res[:10]) == self.orig[:10] + assert list(self.res[:10]) == self.orig[:10] + assert list(self.res[10:40:3]) == self.orig[10:40:3] + assert list(self.res[-5:]) == self.orig[-5:] + + def test_aggregate(self): + assert self.res.count() == 100 + assert self.res.filter(foo.c.bar<30).min(foo.c.bar) == 0 + assert self.res.filter(foo.c.bar<30).max(foo.c.bar) == 29 + # this one fails in mysql as the result comes back as a string + assert self.res.filter(foo.c.bar<30).sum(foo.c.bar) == 435 + # this one fails with postgres, the floating point comparison fails + assert self.res.filter(foo.c.bar<30).avg(foo.c.bar) == 14.5 + + def test_filter(self): + assert self.res.count() == 100 + assert self.res.filter(Foo.c.bar < 30).count() == 30 + res2 = self.res.filter(Foo.c.bar < 30).filter(Foo.c.bar > 10) + assert res2.count() == 19 + + def test_order_by(self): + assert self.res.order_by([Foo.c.bar])[0].bar == 0 + assert self.res.order_by([desc(Foo.c.bar)])[0].bar == 99 + + def test_offset(self): + assert list(self.res.order_by([Foo.c.bar]).offset(10))[0].bar == 10 + + def test_offset(self): + assert len(list(self.res.limit(10))) == 10 + + +if __name__ == "__main__": + testbase.main() </ins></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-22 21:14:53
|
<!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>[1319] sqlalchemy/branches/schema/lib/sqlalchemy/mapping: dev</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1319</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-22 16:14:43 -0500 (Sat, 22 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>dev</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingpropertiespy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemymappingmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py (1318 => 1319)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py 2006-04-22 20:21:50 UTC (rev 1318) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py 2006-04-22 21:14:43 UTC (rev 1319) </span><span class="lines">@@ -354,7 +354,7 @@ </span><span class="cx"> if kwargs.has_key('_sa_session'): </span><span class="cx"> session = kwargs.pop('_sa_session') </span><span class="cx"> else: </span><del>- session = objectstore.get_session(raiseerror=False) </del><ins>+ session = objectstore.get_session(self, raiseerror=False) </ins><span class="cx"> if session is not None: </span><span class="cx"> if not nohist: </span><span class="cx"> # register new with the correct session, before the object's </span><span class="lines">@@ -713,9 +713,6 @@ </span><span class="cx"> # including modifying any of its related items lists, as its already </span><span class="cx"> # been exposed to being modified by the application. </span><span class="cx"> </span><del>- if session is None: - session = objectstore.get_session() - </del><span class="cx"> populate_existing = populate_existing or self.always_refresh </span><span class="cx"> identitykey = self._identity_key(row) </span><span class="cx"> if session.has_key(identitykey): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py (1318 => 1319)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py 2006-04-22 20:21:50 UTC (rev 1318) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py 2006-04-22 21:14:43 UTC (rev 1319) </span><span class="lines">@@ -76,7 +76,7 @@ </span><span class="cx"> return object_mapper(instance).props[self.key].setup_loader(instance) </span><span class="cx"> def lazyload(): </span><span class="cx"> session = objectstore.get_session(instance) </span><del>- connection = session.connect(self.parent) </del><ins>+ connection = session.connection(self.parent) </ins><span class="cx"> clause = sql.and_() </span><span class="cx"> try: </span><span class="cx"> pk = self.parent.pks_by_table[self.columns[0].table] </span><span class="lines">@@ -96,7 +96,7 @@ </span><span class="cx"> if prop is self: </span><span class="cx"> continue </span><span class="cx"> instance.__dict__[prop.key] = row[prop.columns[0]] </span><del>- objectstore.global_attributes.create_history(instance, prop.key, uselist=False, cascade=self.cascade, trackparent=True) </del><ins>+ objectstore.global_attributes.create_history(instance, prop.key, uselist=False) </ins><span class="cx"> return row[self.columns[0]] </span><span class="cx"> else: </span><span class="cx"> return connection.scalar(sql.select([self.columns[0]], clause, use_labels=True),None) </span><span class="lines">@@ -108,7 +108,7 @@ </span><span class="cx"> def execute(self, session, instance, row, identitykey, imap, isnew): </span><span class="cx"> if isnew: </span><span class="cx"> if not self.is_primary(): </span><del>- objectstore.global_attributes.create_history(instance, self.key, False, callable_=self.setup_loader(instance), cascade=self.cascade, trackparent=True) </del><ins>+ objectstore.global_attributes.create_history(instance, self.key, False, callable_=self.setup_loader(instance)) </ins><span class="cx"> else: </span><span class="cx"> objectstore.global_attributes.reset_history(instance, self.key) </span><span class="cx"> </span></span></pre> </div> </div> </body> </html> |
<!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>[1318] sqlalchemy/branches/schema/test: added "hasparent" awareness to attributes package, allows delete-orphan to be cascaded at flush-time instead of operation time</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1318</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-22 15:21:50 -0500 (Sat, 22 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>added "hasparent" awareness to attributes package, allows delete-orphan to be cascaded at flush-time instead of operation time factored out all dependency processing from properties.py into dependency.py</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyattributespy">sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingobjectstorepy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingpropertiespy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingunitofworkpy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py</a></li> <li><a href="#sqlalchemybranchesschematestmasscreatepy">sqlalchemy/branches/schema/test/masscreate.py</a></li> <li><a href="#sqlalchemybranchesschematestobjectstorepy">sqlalchemy/branches/schema/test/objectstore.py</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingdependencypy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/dependency.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemyattributespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py (1317 => 1318)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py 2006-04-22 19:01:37 UTC (rev 1317) +++ sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py 2006-04-22 20:21:50 UTC (rev 1318) </span><span class="lines">@@ -89,7 +89,11 @@ </span><span class="cx"> return self </span><span class="cx"> def plain_init(self, *args, **kwargs): </span><span class="cx"> pass </span><del>- </del><ins>+ def hasparent(self, item): + return item.__class__._attribute_manager.attribute_history(item).get('_hasparent_' + self.key) + def sethasparent(self, item, value): + item.__class__._attribute_manager.attribute_history(item)['_hasparent_' + self.key] = value + </ins><span class="cx"> class ScalarAttribute(ManagedAttribute): </span><span class="cx"> """Used by AttributeManager to track the history of a scalar attribute </span><span class="cx"> on an object instance. This is the "scalar history container" object. </span><span class="lines">@@ -97,10 +101,11 @@ </span><span class="cx"> so that the two objects can be called upon largely interchangeably.""" </span><span class="cx"> # make our own NONE to distinguish from "None" </span><span class="cx"> NONE = object() </span><del>- def __init__(self, obj, key, extension=None, **kwargs): </del><ins>+ def __init__(self, obj, key, extension=None, trackparent=False, **kwargs): </ins><span class="cx"> ManagedAttribute.__init__(self, obj, key) </span><span class="cx"> self.orig = ScalarAttribute.NONE </span><span class="cx"> self.extension = extension </span><ins>+ self.trackparent = trackparent </ins><span class="cx"> def clear(self): </span><span class="cx"> del self.obj.__dict__[self.key] </span><span class="cx"> def history_contains(self, obj): </span><span class="lines">@@ -120,6 +125,8 @@ </span><span class="cx"> if self.orig is ScalarAttribute.NONE: </span><span class="cx"> self.orig = orig </span><span class="cx"> self.obj.__dict__[self.key] = value </span><ins>+ if value is not None and self.trackparent: + self.sethasparent(value, True) </ins><span class="cx"> if self.extension is not None: </span><span class="cx"> self.extension.set(self.obj, value, orig) </span><span class="cx"> self.value_changed(orig, value) </span><span class="lines">@@ -128,6 +135,8 @@ </span><span class="cx"> if self.orig is ScalarAttribute.NONE: </span><span class="cx"> self.orig = orig </span><span class="cx"> self.obj.__dict__[self.key] = None </span><ins>+ if self.trackparent: + self.sethasparent(orig, False) </ins><span class="cx"> if self.extension is not None: </span><span class="cx"> self.extension.set(self.obj, None, orig) </span><span class="cx"> self.value_changed(orig, None) </span><span class="lines">@@ -164,9 +173,10 @@ </span><span class="cx"> This is the "list history container" object. </span><span class="cx"> Subclasses util.HistoryArraySet to provide "onchange" event handling as well </span><span class="cx"> as a plugin point for BackrefExtension objects.""" </span><del>- def __init__(self, obj, key, data=None, extension=None, **kwargs): </del><ins>+ def __init__(self, obj, key, data=None, extension=None, trackparent=False, **kwargs): </ins><span class="cx"> ManagedAttribute.__init__(self, obj, key) </span><span class="cx"> self.extension = extension </span><ins>+ self.trackparent = trackparent </ins><span class="cx"> # if we are given a list, try to behave nicely with an existing </span><span class="cx"> # list that might be set on the object already </span><span class="cx"> try: </span><span class="lines">@@ -194,6 +204,8 @@ </span><span class="cx"> def _setrecord(self, item): </span><span class="cx"> res = util.HistoryArraySet._setrecord(self, item) </span><span class="cx"> if res: </span><ins>+ if self.trackparent: + self.sethasparent(item, True) </ins><span class="cx"> self.value_changed(self.obj, self.key, item, self, False) </span><span class="cx"> if self.extension is not None: </span><span class="cx"> self.extension.append(self.obj, item) </span><span class="lines">@@ -201,6 +213,8 @@ </span><span class="cx"> def _delrecord(self, item): </span><span class="cx"> res = util.HistoryArraySet._delrecord(self, item) </span><span class="cx"> if res: </span><ins>+ if self.trackparent: + self.sethasparent(item, False) </ins><span class="cx"> self.value_changed(self.obj, self.key, item, self, True) </span><span class="cx"> if self.extension is not None: </span><span class="cx"> self.extension.delete(self.obj, item) </span><span class="lines">@@ -365,7 +379,8 @@ </span><span class="cx"> try: </span><span class="cx"> attributes = self.attribute_history(o) </span><span class="cx"> for hist in attributes.values(): </span><del>- hist.rollback() </del><ins>+ if isinstance(hist, ManagedAttribute): + hist.rollback() </ins><span class="cx"> except KeyError: </span><span class="cx"> pass </span><span class="cx"> o._managed_value_changed = False </span><span class="lines">@@ -377,7 +392,8 @@ </span><span class="cx"> try: </span><span class="cx"> attributes = self.attribute_history(o) </span><span class="cx"> for hist in attributes.values(): </span><del>- hist.commit() </del><ins>+ if isinstance(hist, ManagedAttribute): + hist.commit() </ins><span class="cx"> except KeyError: </span><span class="cx"> pass </span><span class="cx"> o._managed_value_changed = False </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingdependencypy"></a> <div class="addfile"><h4>Added: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/dependency.py (1317 => 1318)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/dependency.py 2006-04-22 19:01:37 UTC (rev 1317) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/dependency.py 2006-04-22 20:21:50 UTC (rev 1318) </span><span class="lines">@@ -0,0 +1,268 @@ </span><ins>+"""bridges the PropertyLoader (i.e. a relation()) and the UOWTransaction +together to allow processing of scalar- and list-based dependencies at flush time.""" + +from sync import ONETOMANY,MANYTOONE,MANYTOMANY +from sqlalchemy import sql + +class DependencyProcessor(object): + def __init__(self, key, syncrules, cascade, secondary=None, association=None, is_backref=False, post_update=False): + # TODO: update instance variable names to be more meaningful + self.syncrules = syncrules + self.cascade = cascade + self.mapper = syncrules.child_mapper + self.parent = syncrules.parent_mapper + self.association = association + self.secondary = secondary + self.direction = syncrules.direction + self.is_backref = is_backref + self.post_update = post_update + self.key = key + + class MapperStub(object): + """poses as a Mapper representing the association table in a many-to-many + join, when performing a commit(). + + The Task objects in the objectstore module treat it just like + any other Mapper, but in fact it only serves as a "dependency" placeholder + for the many-to-many update task.""" + def __init__(self, mapper): + self.mapper = mapper + def save_obj(self, *args, **kwargs): + pass + def delete_obj(self, *args, **kwargs): + pass + def _primary_mapper(self): + return self + + def register_dependencies(self, uowcommit): + """tells a UOWTransaction what mappers are dependent on which, with regards + to the two or three mappers handled by this PropertyLoader. + + Also registers itself as a "processor" for one of its mappers, which + will be executed after that mapper's objects have been saved or before + they've been deleted. The process operation manages attributes and dependent + operations upon the objects of one of the involved mappers.""" + if self.association is not None: + # association object. our mapper should be dependent on both + # the parent mapper and the association object mapper. + # this is where we put the "stub" as a marker, so we get + # association/parent->stub->self, then we process the child + # elments after the 'stub' save, which is before our own + # mapper's save. + stub = DependencyProcessor.MapperStub(self.association) + uowcommit.register_dependency(self.parent, stub) + uowcommit.register_dependency(self.association, stub) + uowcommit.register_dependency(stub, self.mapper) + uowcommit.register_processor(stub, self, self.parent, False) + uowcommit.register_processor(stub, self, self.parent, True) + + elif self.direction == MANYTOMANY: + # many-to-many. create a "Stub" mapper to represent the + # "middle table" in the relationship. This stub mapper doesnt save + # or delete any objects, but just marks a dependency on the two + # related mappers. its dependency processor then populates the + # association table. + + if self.is_backref: + # if we are the "backref" half of a two-way backref + # relationship, let the other mapper handle inserting the rows + return + stub = DependencyProcessor.MapperStub(self.mapper) + uowcommit.register_dependency(self.parent, stub) + uowcommit.register_dependency(self.mapper, stub) + uowcommit.register_processor(stub, self, self.parent, False) + uowcommit.register_processor(stub, self, self.parent, True) + elif self.direction == ONETOMANY: + if self.post_update: + stub = DependencyProcessor.MapperStub(self.mapper) + uowcommit.register_dependency(self.mapper, stub) + uowcommit.register_dependency(self.parent, stub) + uowcommit.register_processor(stub, self, self.parent, False) + uowcommit.register_processor(stub, self, self.parent, True) + else: + uowcommit.register_dependency(self.parent, self.mapper) + uowcommit.register_processor(self.parent, self, self.parent, False) + uowcommit.register_processor(self.parent, self, self.parent, True) + elif self.direction == MANYTOONE: + if self.post_update: + stub = DependencyProcessor.MapperStub(self.mapper) + uowcommit.register_dependency(self.mapper, stub) + uowcommit.register_dependency(self.parent, stub) + uowcommit.register_processor(stub, self, self.parent, False) + uowcommit.register_processor(stub, self, self.parent, True) + else: + uowcommit.register_dependency(self.mapper, self.parent) + uowcommit.register_processor(self.mapper, self, self.parent, False) + uowcommit.register_processor(self.mapper, self, self.parent, True) + else: + raise AssertionError(" no foreign key ?") + + # TODO: this method should be moved to an external object + def get_object_dependencies(self, obj, uowcommit, passive = True): + return uowcommit.uow.attributes.get_history(obj, self.key, passive = passive) + + # TODO: this method should be moved to an external object + def whose_dependent_on_who(self, obj1, obj2): + """given an object pair assuming obj2 is a child of obj1, returns a tuple + with the dependent object second, or None if they are equal. + used by objectstore's object-level topological sort (i.e. cyclical + table dependency).""" + if obj1 is obj2: + return None + elif self.direction == ONETOMANY: + return (obj1, obj2) + else: + return (obj2, obj1) + + # TODO: this method should be moved to an external object + def process_dependencies(self, task, deplist, uowcommit, delete = False): + """this method is called during a commit operation to synchronize data between a parent and child object. + it also can establish child or parent objects within the unit of work as "to be saved" or "deleted" + in some cases.""" + #print self.mapper.table.name + " " + self.key + " " + repr(len(deplist)) + " process_dep isdelete " + repr(delete) + " direction " + repr(self.direction) + + def getlist(obj, passive=True): + return self.get_object_dependencies(obj, uowcommit, passive) + + connection = uowcommit.transaction.connection(self.mapper) + + # plugin point + + if self.direction == MANYTOMANY: + secondary_delete = [] + secondary_insert = [] + if delete: + for obj in deplist: + childlist = getlist(obj, False) + for child in childlist.deleted_items() + childlist.unchanged_items(): + associationrow = {} + self._synchronize(obj, child, associationrow, False) + secondary_delete.append(associationrow) + else: + for obj in deplist: + childlist = getlist(obj) + if childlist is None: continue + for child in childlist.added_items(): + associationrow = {} + self._synchronize(obj, child, associationrow, False) + secondary_insert.append(associationrow) + for child in childlist.deleted_items(): + associationrow = {} + self._synchronize(obj, child, associationrow, False) + secondary_delete.append(associationrow) + if len(secondary_delete): + # TODO: precompile the delete/insert queries and store them as instance variables + # on the PropertyLoader + statement = self.secondary.delete(sql.and_(*[c == sql.bindparam(c.key) for c in self.secondary.c])) + connection.execute(statement, secondary_delete) + if len(secondary_insert): + statement = self.secondary.insert() + connection.execute(statement, secondary_insert) + elif self.direction == MANYTOONE and delete: + if self.cascade.delete_orphan: + for obj in deplist: + childlist = getlist(obj, False) + for child in childlist.deleted_items() + childlist.unchanged_items(): + if child is not None and childlist.hasparent(child) is False: + uowcommit.register_object(child, isdelete=True) + elif self.post_update: + # post_update means we have to update our row to not reference the child object + # before we can DELETE the row + for obj in deplist: + self._synchronize(obj, None, None, True) + uowcommit.register_object(obj, postupdate=True) + elif self.direction == ONETOMANY and delete: + # head object is being deleted, and we manage its list of child objects + # the child objects have to have their foreign key to the parent set to NULL + if self.cascade.delete_orphan and not self.post_update: + for obj in deplist: + childlist = getlist(obj, False) + for child in childlist.deleted_items(): + if child is not None and childlist.hasparent(child) is False: + uowcommit.register_object(child, isdelete=True) + for child in childlist.unchanged_items(): + if child is not None: + uowcommit.register_object(child, isdelete=True) + else: + for obj in deplist: + childlist = getlist(obj, False) + for child in childlist.deleted_items(): + if child is not None and childlist.hasparent(child) is False: + self._synchronize(obj, child, None, True) + uowcommit.register_object(child, postupdate=self.post_update) + for child in childlist.unchanged_items(): + if child is not None: + self._synchronize(obj, child, None, True) + uowcommit.register_object(child, postupdate=self.post_update) + elif self.association is not None: + # manage association objects. + for obj in deplist: + childlist = getlist(obj, passive=True) + if childlist is None: continue + + #print "DIRECTION", self.direction + d = {} + for child in childlist: + self._synchronize(obj, child, None, False) + key = self.mapper.instance_key(child) + #print "SYNCHRONIZED", child, "INSTANCE KEY", key + d[key] = child + uowcommit.unregister_object(child) + + for child in childlist.added_items(): + uowcommit.register_object(child) + key = self.mapper.instance_key(child) + #print "ADDED, INSTANCE KEY", key + d[key] = child + + for child in childlist.unchanged_items(): + key = self.mapper.instance_key(child) + o = d[key] + o._instance_key= key + + for child in childlist.deleted_items(): + key = self.mapper.instance_key(child) + #print "DELETED, INSTANCE KEY", key + if d.has_key(key): + o = d[key] + o._instance_key = key + uowcommit.unregister_object(child) + else: + #print "DELETE ASSOC OBJ", repr(child) + uowcommit.register_object(child, isdelete=True) + else: + for obj in deplist: + childlist = getlist(obj, passive=True) + if childlist is not None: + for child in childlist.added_items(): + self._synchronize(obj, child, None, False) + if self.direction == ONETOMANY and child is not None: + uowcommit.register_object(child, postupdate=self.post_update) + if self.direction == MANYTOONE: + uowcommit.register_object(obj, postupdate=self.post_update) + else: + for child in childlist.deleted_items(): + if not self.cascade.delete_orphan: + self._synchronize(obj, child, None, True) + uowcommit.register_object(child, isdelete=False) + elif childlist.hasparent(child) is False: + uowcommit.register_object(child, isdelete=True) + + # TODO: this method should be moved to an external object + def _synchronize(self, obj, child, associationrow, clearkeys): + """called during a commit to execute the full list of syncrules on the + given object/child/optional association row""" + if self.direction == ONETOMANY: + source = obj + dest = child + elif self.direction == MANYTOONE: + source = child + dest = obj + elif self.direction == MANYTOMANY: + dest = associationrow + source = None + + if dest is None: + return + + self.syncrules.execute(source, dest, obj, child, clearkeys) </ins></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py (1317 => 1318)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py 2006-04-22 19:01:37 UTC (rev 1317) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py 2006-04-22 20:21:50 UTC (rev 1318) </span><span class="lines">@@ -338,7 +338,6 @@ </span><span class="cx"> the keyword argument 'entity_name' can also be provided which will be used by the import.""" </span><span class="cx"> for o in obj: </span><span class="cx"> for c in object_mapper(o, **kwargs).cascade_iterator('delete', o): </span><del>- print "CASCADING DELETE TO", c </del><span class="cx"> if not self._is_bound(c): </span><span class="cx"> c = self.import_(c, **kwargs) </span><span class="cx"> self.uow.register_deleted(c) </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py (1317 => 1318)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py 2006-04-22 19:01:37 UTC (rev 1317) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py 2006-04-22 20:21:50 UTC (rev 1318) </span><span class="lines">@@ -17,6 +17,7 @@ </span><span class="cx"> import sync </span><span class="cx"> import mapper </span><span class="cx"> import objectstore </span><ins>+import dependency </ins><span class="cx"> import util as mapperutil </span><span class="cx"> from sqlalchemy.exceptions import * </span><span class="cx"> import sets </span><span class="lines">@@ -95,7 +96,7 @@ </span><span class="cx"> if prop is self: </span><span class="cx"> continue </span><span class="cx"> instance.__dict__[prop.key] = row[prop.columns[0]] </span><del>- objectstore.global_attributes.create_history(instance, prop.key, uselist=False, cascade=self.cascade) </del><ins>+ objectstore.global_attributes.create_history(instance, prop.key, uselist=False, cascade=self.cascade, trackparent=True) </ins><span class="cx"> return row[self.columns[0]] </span><span class="cx"> else: </span><span class="cx"> return connection.scalar(sql.select([self.columns[0]], clause, use_labels=True),None) </span><span class="lines">@@ -107,7 +108,7 @@ </span><span class="cx"> def execute(self, session, instance, row, identitykey, imap, isnew): </span><span class="cx"> if isnew: </span><span class="cx"> if not self.is_primary(): </span><del>- objectstore.global_attributes.create_history(instance, self.key, False, callable_=self.setup_loader(instance), cascade=self.cascade) </del><ins>+ objectstore.global_attributes.create_history(instance, self.key, False, callable_=self.setup_loader(instance), cascade=self.cascade, trackparent=True) </ins><span class="cx"> else: </span><span class="cx"> objectstore.global_attributes.reset_history(instance, self.key) </span><span class="cx"> </span><span class="lines">@@ -120,7 +121,7 @@ </span><span class="cx"> </span><span class="cx"> """describes an object property that holds a single item or list of items that correspond </span><span class="cx"> to a related database table.""" </span><del>- def __init__(self, argument, secondary, primaryjoin, secondaryjoin, foreignkey=None, uselist=None, private=False, association=None, use_alias=None, selectalias=None, order_by=False, attributeext=None, backref=None, is_backref=False, post_update=False, cascade=None): </del><ins>+ def __init__(self, argument, secondary, primaryjoin, secondaryjoin, foreignkey=None, uselist=None, private=False, association=None, order_by=False, attributeext=None, backref=None, is_backref=False, post_update=False, cascade=None): </ins><span class="cx"> self.uselist = uselist </span><span class="cx"> self.argument = argument </span><span class="cx"> self.secondary = secondary </span><span class="lines">@@ -131,7 +132,7 @@ </span><span class="cx"> </span><span class="cx"> # would like to have foreignkey be a list. </span><span class="cx"> # however, have to figure out how to do </span><del>- # <column> in <list>, since column overrides the == operator or somethign </del><ins>+ # <column> in <list>, since column overrides the == operator </ins><span class="cx"> # and it doesnt work </span><span class="cx"> self.foreignkey = foreignkey #util.to_set(foreignkey) </span><span class="cx"> if foreignkey: </span><span class="lines">@@ -148,10 +149,6 @@ </span><span class="cx"> self.cascade = mapperutil.CascadeOptions() </span><span class="cx"> </span><span class="cx"> self.association = association </span><del>- if selectalias is not None: - print "'selectalias' argument to relation() is deprecated. eager loads automatically alias-ize tables now." - if use_alias is not None: - print "'use_alias' argument to relation() is deprecated. eager loads automatically alias-ize tables now." </del><span class="cx"> self.order_by = order_by </span><span class="cx"> self.attributeext=attributeext </span><span class="cx"> if isinstance(backref, str): </span><span class="lines">@@ -163,10 +160,8 @@ </span><span class="cx"> private = property(lambda s:s.cascade.delete_orphan) </span><span class="cx"> </span><span class="cx"> def cascade_iterator(self, type, object, recursive=None): </span><del>- </del><span class="cx"> if not type in self.cascade: </span><span class="cx"> return </span><del>- </del><span class="cx"> if recursive is None: </span><span class="cx"> recursive = sets.Set() </span><span class="cx"> </span><span class="lines">@@ -231,13 +226,14 @@ </span><span class="cx"> if self.direction is None: </span><span class="cx"> self.direction = self._get_direction() </span><span class="cx"> </span><del>- if self.uselist is None and self.direction == PropertyLoader.MANYTOONE: </del><ins>+ if self.uselist is None and self.direction == sync.MANYTOONE: </ins><span class="cx"> self.uselist = False </span><span class="cx"> </span><span class="cx"> if self.uselist is None: </span><span class="cx"> self.uselist = True </span><span class="cx"> </span><span class="cx"> self._compile_synchronizers() </span><ins>+ self._dependency_processor = dependency.DependencyProcessor(self.key, self.syncrules, self.cascade, secondary=self.secondary, association=self.association, is_backref=self.is_backref, post_update=self.post_update) </ins><span class="cx"> </span><span class="cx"> # primary property handler, set up class attributes </span><span class="cx"> if self.is_primary(): </span><span class="lines">@@ -258,23 +254,23 @@ </span><span class="cx"> </span><span class="cx"> def _set_class_attribute(self, class_, key): </span><span class="cx"> """sets attribute behavior on our target class.""" </span><del>- objectstore.global_attributes.register_attribute(class_, key, uselist = self.uselist, extension=self.attributeext, cascade=self.cascade) </del><ins>+ objectstore.global_attributes.register_attribute(class_, key, uselist = self.uselist, extension=self.attributeext, cascade=self.cascade, trackparent=True) </ins><span class="cx"> </span><span class="cx"> def _get_direction(self): </span><span class="cx"> """determines our 'direction', i.e. do we represent one to many, many to many, etc.""" </span><span class="cx"> #print self.key, repr(self.parent.table.name), repr(self.parent.primarytable.name), repr(self.foreignkey.table.name), repr(self.target), repr(self.foreigntable.name) </span><span class="cx"> </span><span class="cx"> if self.secondaryjoin is not None: </span><del>- return PropertyLoader.MANYTOMANY </del><ins>+ return sync.MANYTOMANY </ins><span class="cx"> elif self.parent.table is self.target: </span><span class="cx"> if self.foreignkey.primary_key: </span><del>- return PropertyLoader.MANYTOONE </del><ins>+ return sync.MANYTOONE </ins><span class="cx"> else: </span><del>- return PropertyLoader.ONETOMANY </del><ins>+ return sync.ONETOMANY </ins><span class="cx"> elif self.foreigntable == self.mapper.noninherited_table: </span><del>- return PropertyLoader.ONETOMANY </del><ins>+ return sync.ONETOMANY </ins><span class="cx"> elif self.foreigntable == self.parent.noninherited_table: </span><del>- return PropertyLoader.MANYTOONE </del><ins>+ return sync.MANYTOONE </ins><span class="cx"> else: </span><span class="cx"> raise ArgumentError("Cant determine relation direction") </span><span class="cx"> </span><span class="lines">@@ -334,251 +330,15 @@ </span><span class="cx"> return c.copy_container() </span><span class="cx"> return None </span><span class="cx"> </span><del>- - # TODO: this should be moved to an external object - class MapperStub(object): - """poses as a Mapper representing the association table in a many-to-many - join, when performing a commit(). - - The Task objects in the objectstore module treat it just like - any other Mapper, but in fact it only serves as a "dependency" placeholder - for the many-to-many update task.""" - def __init__(self, mapper): - self.mapper = mapper - def save_obj(self, *args, **kwargs): - pass - def delete_obj(self, *args, **kwargs): - pass - def _primary_mapper(self): - return self - - # TODO: this method should be moved to an external object - def register_dependencies(self, uowcommit): - """tells a UOWTransaction what mappers are dependent on which, with regards - to the two or three mappers handled by this PropertyLoader. - - Also registers itself as a "processor" for one of its mappers, which - will be executed after that mapper's objects have been saved or before - they've been deleted. The process operation manages attributes and dependent - operations upon the objects of one of the involved mappers.""" - if self.association is not None: - # association object. our mapper should be dependent on both - # the parent mapper and the association object mapper. - # this is where we put the "stub" as a marker, so we get - # association/parent->stub->self, then we process the child - # elments after the 'stub' save, which is before our own - # mapper's save. - stub = PropertyLoader.MapperStub(self.association) - uowcommit.register_dependency(self.parent, stub) - uowcommit.register_dependency(self.association, stub) - uowcommit.register_dependency(stub, self.mapper) - uowcommit.register_processor(stub, self, self.parent, False) - uowcommit.register_processor(stub, self, self.parent, True) - - elif self.direction == PropertyLoader.MANYTOMANY: - # many-to-many. create a "Stub" mapper to represent the - # "middle table" in the relationship. This stub mapper doesnt save - # or delete any objects, but just marks a dependency on the two - # related mappers. its dependency processor then populates the - # association table. - - if self.is_backref: - # if we are the "backref" half of a two-way backref - # relationship, let the other mapper handle inserting the rows - return - stub = PropertyLoader.MapperStub(self.mapper) - uowcommit.register_dependency(self.parent, stub) - uowcommit.register_dependency(self.mapper, stub) - uowcommit.register_processor(stub, self, self.parent, False) - uowcommit.register_processor(stub, self, self.parent, True) - elif self.direction == PropertyLoader.ONETOMANY: - if self.post_update: - stub = PropertyLoader.MapperStub(self.mapper) - uowcommit.register_dependency(self.mapper, stub) - uowcommit.register_dependency(self.parent, stub) - uowcommit.register_processor(stub, self, self.parent, False) - uowcommit.register_processor(stub, self, self.parent, True) - else: - uowcommit.register_dependency(self.parent, self.mapper) - uowcommit.register_processor(self.parent, self, self.parent, False) - uowcommit.register_processor(self.parent, self, self.parent, True) - elif self.direction == PropertyLoader.MANYTOONE: - if self.post_update: - stub = PropertyLoader.MapperStub(self.mapper) - uowcommit.register_dependency(self.mapper, stub) - uowcommit.register_dependency(self.parent, stub) - uowcommit.register_processor(stub, self, self.parent, False) - uowcommit.register_processor(stub, self, self.parent, True) - else: - uowcommit.register_dependency(self.mapper, self.parent) - uowcommit.register_processor(self.mapper, self, self.parent, False) - uowcommit.register_processor(self.mapper, self, self.parent, True) - else: - raise AssertionError(" no foreign key ?") - - # TODO: this method should be moved to an external object - def get_object_dependencies(self, obj, uowcommit, passive = True): - return uowcommit.uow.attributes.get_history(obj, self.key, passive = passive) - - # TODO: this method should be moved to an external object - def whose_dependent_on_who(self, obj1, obj2): - """given an object pair assuming obj2 is a child of obj1, returns a tuple - with the dependent object second, or None if they are equal. - used by objectstore's object-level topological sort (i.e. cyclical - table dependency).""" - if obj1 is obj2: - return None - elif self.direction == PropertyLoader.ONETOMANY: - return (obj1, obj2) - else: - return (obj2, obj1) - - # TODO: this method should be moved to an external object - def process_dependencies(self, task, deplist, uowcommit, delete = False): - """this method is called during a commit operation to synchronize data between a parent and child object. - it also can establish child or parent objects within the unit of work as "to be saved" or "deleted" - in some cases.""" - #print self.mapper.table.name + " " + self.key + " " + repr(len(deplist)) + " process_dep isdelete " + repr(delete) + " direction " + repr(self.direction) - - def getlist(obj, passive=True): - return self.get_object_dependencies(obj, uowcommit, passive) - - connection = uowcommit.transaction.connection(self.mapper) - - # plugin point - - if self.direction == PropertyLoader.MANYTOMANY: - secondary_delete = [] - secondary_insert = [] - if delete: - for obj in deplist: - childlist = getlist(obj, False) - for child in childlist.deleted_items() + childlist.unchanged_items(): - associationrow = {} - self._synchronize(obj, child, associationrow, False) - secondary_delete.append(associationrow) - else: - for obj in deplist: - childlist = getlist(obj) - if childlist is None: continue - for child in childlist.added_items(): - associationrow = {} - self._synchronize(obj, child, associationrow, False) - secondary_insert.append(associationrow) - for child in childlist.deleted_items(): - associationrow = {} - self._synchronize(obj, child, associationrow, False) - secondary_delete.append(associationrow) - if len(secondary_delete): - # TODO: precompile the delete/insert queries and store them as instance variables - # on the PropertyLoader - statement = self.secondary.delete(sql.and_(*[c == sql.bindparam(c.key) for c in self.secondary.c])) - connection.execute(statement, secondary_delete) - if len(secondary_insert): - statement = self.secondary.insert() - connection.execute(statement, secondary_insert) - elif self.direction == PropertyLoader.MANYTOONE and delete: - if self.private: - for obj in deplist: - childlist = getlist(obj, False) - for child in childlist.deleted_items() + childlist.unchanged_items(): - if child is None: - continue - # if private child object, and is in the uow's "deleted" list, - # insure its in the list of items to be deleted - if child in uowcommit.uow.deleted: - uowcommit.register_object(child, isdelete=True) - elif self.post_update: - # post_update means we have to update our row to not reference the child object - # before we can DELETE the row - for obj in deplist: - self._synchronize(obj, None, None, True) - uowcommit.register_object(obj, postupdate=True) - elif self.direction == PropertyLoader.ONETOMANY and delete: - # head object is being deleted, and we manage its list of child objects - # the child objects have to have their foreign key to the parent set to NULL - if self.private and not self.post_update: - for obj in deplist: - print "HI ON", obj - childlist = getlist(obj, False) - for child in childlist.deleted_items() + childlist.unchanged_items(): - if child is None: - continue - # if private child object, and is in the uow's "deleted" list, - # insure its in the list of items to be deleted - print "DEL CHILD", child - if child in uowcommit.uow.deleted: - print "REGISTER", child - uowcommit.register_object(child, isdelete=True) - else: - for obj in deplist: - print "HI 2 ON", obj - childlist = getlist(obj, False) - for child in childlist.deleted_items() + childlist.unchanged_items(): - print "DELCHILD", child - if child is not None: - self._synchronize(obj, child, None, True) - uowcommit.register_object(child, postupdate=self.post_update) - elif self.association is not None: - # manage association objects. - for obj in deplist: - childlist = getlist(obj, passive=True) - if childlist is None: continue - - #print "DIRECTION", self.direction - d = {} - for child in childlist: - self._synchronize(obj, child, None, False) - key = self.mapper.instance_key(child) - #print "SYNCHRONIZED", child, "INSTANCE KEY", key - d[key] = child - uowcommit.unregister_object(child) - - for child in childlist.added_items(): - uowcommit.register_object(child) - key = self.mapper.instance_key(child) - #print "ADDED, INSTANCE KEY", key - d[key] = child - - for child in childlist.unchanged_items(): - key = self.mapper.instance_key(child) - o = d[key] - o._instance_key= key - - for child in childlist.deleted_items(): - key = self.mapper.instance_key(child) - #print "DELETED, INSTANCE KEY", key - if d.has_key(key): - o = d[key] - o._instance_key = key - uowcommit.unregister_object(child) - else: - #print "DELETE ASSOC OBJ", repr(child) - uowcommit.register_object(child, isdelete=True) - else: - for obj in deplist: - childlist = getlist(obj, passive=True) - if childlist is not None: - for child in childlist.added_items(): - print "ADDCHILD", child - self._synchronize(obj, child, None, False) - if self.direction == PropertyLoader.ONETOMANY and child is not None: - uowcommit.register_object(child, postupdate=self.post_update) - if self.direction == PropertyLoader.MANYTOONE: - uowcommit.register_object(obj, postupdate=self.post_update) - if self.direction != PropertyLoader.MANYTOONE: - for child in childlist.deleted_items(): - if not self.private: - self._synchronize(obj, child, None, True) - uowcommit.register_object(child, isdelete=self.private) - </del><span class="cx"> def execute(self, session, instance, row, identitykey, imap, isnew): </span><span class="cx"> if self.is_primary(): </span><span class="cx"> return </span><span class="cx"> #print "PLAIN PROPLOADER EXEC NON-PRIAMRY", repr(id(self)), repr(self.mapper.class_), self.key </span><del>- objectstore.global_attributes.create_history(instance, self.key, self.uselist, cascade=self.cascade) </del><ins>+ objectstore.global_attributes.create_history(instance, self.key, self.uselist, cascade=self.cascade, trackparent=True) </ins><span class="cx"> </span><del>- # TODO: this method should be moved to an external object </del><ins>+ def register_dependencies(self, uowcommit): + self._dependency_processor.register_dependencies(uowcommit) + </ins><span class="cx"> def _compile_synchronizers(self): </span><span class="cx"> """assembles a list of 'synchronization rules', which are instructions on how to populate </span><span class="cx"> the objects on each side of a relationship. This is done when a PropertyLoader is </span><span class="lines">@@ -586,38 +346,17 @@ </span><span class="cx"> </span><span class="cx"> The list of rules is used within commits by the _synchronize() method when dependent </span><span class="cx"> objects are processed.""" </span><del>- - </del><span class="cx"> parent_tables = util.HashSet(self.parent.tables + [self.parent.primarytable]) </span><span class="cx"> target_tables = util.HashSet(self.mapper.tables + [self.mapper.primarytable]) </span><span class="cx"> </span><span class="cx"> self.syncrules = sync.ClauseSynchronizer(self.parent, self.mapper, self.direction) </span><del>- if self.direction == PropertyLoader.MANYTOMANY: </del><ins>+ if self.direction == sync.MANYTOMANY: </ins><span class="cx"> #print "COMPILING p/c", self.parent, self.mapper </span><span class="cx"> self.syncrules.compile(self.primaryjoin, parent_tables, [self.secondary], False) </span><span class="cx"> self.syncrules.compile(self.secondaryjoin, target_tables, [self.secondary], True) </span><span class="cx"> else: </span><span class="cx"> self.syncrules.compile(self.primaryjoin, parent_tables, target_tables) </span><span class="cx"> </span><del>- # TODO: this method should be moved to an external object - def _synchronize(self, obj, child, associationrow, clearkeys): - """called during a commit to execute the full list of syncrules on the - given object/child/optional association row""" - if self.direction == PropertyLoader.ONETOMANY: - source = obj - dest = child - elif self.direction == PropertyLoader.MANYTOONE: - source = child - dest = obj - elif self.direction == PropertyLoader.MANYTOMANY: - dest = associationrow - source = None - - if dest is None: - return - - self.syncrules.execute(source, dest, obj, child, clearkeys) - </del><span class="cx"> class LazyLoader(PropertyLoader): </span><span class="cx"> def do_init_subclass(self, key, parent): </span><span class="cx"> (self.lazywhere, self.lazybinds) = create_lazy_clause(self.parent.noninherited_table, self.primaryjoin, self.secondaryjoin, self.foreignkey) </span><span class="lines">@@ -628,7 +367,7 @@ </span><span class="cx"> def _set_class_attribute(self, class_, key): </span><span class="cx"> # establish a class-level lazy loader on our class </span><span class="cx"> #print "SETCLASSATTR LAZY", repr(class_), key </span><del>- objectstore.global_attributes.register_attribute(class_, key, uselist = self.uselist, callable_=lambda i: self.setup_loader(i), extension=self.attributeext, cascade=self.cascade) </del><ins>+ objectstore.global_attributes.register_attribute(class_, key, uselist = self.uselist, callable_=lambda i: self.setup_loader(i), extension=self.attributeext, cascade=self.cascade, trackparent=True) </ins><span class="cx"> </span><span class="cx"> def setup_loader(self, instance): </span><span class="cx"> if not self.parent.is_assigned(instance): </span><span class="lines">@@ -676,7 +415,7 @@ </span><span class="cx"> #print "EXEC NON-PRIAMRY", repr(self.mapper.class_), self.key </span><span class="cx"> # we are not the primary manager for this attribute on this class - set up a per-instance lazyloader, </span><span class="cx"> # which will override the class-level behavior </span><del>- objectstore.global_attributes.create_history(instance, self.key, self.uselist, callable_=self.setup_loader(instance), cascade=self.cascade) </del><ins>+ objectstore.global_attributes.create_history(instance, self.key, self.uselist, callable_=self.setup_loader(instance), cascade=self.cascade, trackparent=True) </ins><span class="cx"> else: </span><span class="cx"> #print "EXEC PRIMARY", repr(self.mapper.class_), self.key </span><span class="cx"> # we are the primary manager for this attribute on this class - reset its per-instance attribute state, </span><span class="lines">@@ -827,7 +566,7 @@ </span><span class="cx"> if isnew: </span><span class="cx"> # new row loaded from the database. initialize a blank container on the instance. </span><span class="cx"> # this will override any per-class lazyloading type of stuff. </span><del>- h = objectstore.global_attributes.create_history(instance, self.key, self.uselist, cascade=self.cascade) </del><ins>+ h = objectstore.global_attributes.create_history(instance, self.key, self.uselist, cascade=self.cascade, trackparent=True) </ins><span class="cx"> </span><span class="cx"> if not self.uselist: </span><span class="cx"> if isnew: </span><span class="lines">@@ -928,6 +667,7 @@ </span><span class="cx"> # else set one of us as the "backreference" </span><span class="cx"> if not prop.mapper.props[self.key].is_backref: </span><span class="cx"> prop.is_backref=True </span><ins>+ prop._dependency_processor.is_backref=True </ins><span class="cx"> def get_extension(self): </span><span class="cx"> """returns an attribute extension to use with this backreference.""" </span><span class="cx"> return attributes.GenericBackrefExtension(self.key) </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingunitofworkpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py (1317 => 1318)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py 2006-04-22 19:01:37 UTC (rev 1317) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py 2006-04-22 20:21:50 UTC (rev 1318) </span><span class="lines">@@ -44,16 +44,12 @@ </span><span class="cx"> self.cascade = cascade </span><span class="cx"> def do_value_changed(self, obj, key, item, listval, isdelete): </span><span class="cx"> sess = get_session(obj, raiseerror=False) </span><del>- if sess is None: - return - sess._register_dirty(obj) - if self.cascade is not None: - if isdelete: - if self.cascade.delete_orphan: - sess.delete(item) - else: - if self.cascade.save_update: - sess.save_or_update(item) </del><ins>+ if sess is not None: + sess._register_dirty(obj) + if self.cascade is not None: + if not isdelete: + if self.cascade.save_update: + sess.save_or_update(item) </ins><span class="cx"> def append(self, item, _mapper_nohistory = False): </span><span class="cx"> if _mapper_nohistory: </span><span class="cx"> self.append_nohistory(item) </span><span class="lines">@@ -70,9 +66,7 @@ </span><span class="cx"> if sess is not None: </span><span class="cx"> sess._register_dirty(obj) </span><span class="cx"> if self.cascade is not None: </span><del>- if oldvalue is not None and self.cascade.delete_orphan: - sess.delete(oldvalue) - if newvalue is not None and self.cascade.save_update: </del><ins>+ if self.cascade.save_update: </ins><span class="cx"> sess.save_or_update(newvalue) </span><span class="cx"> </span><span class="cx"> class UOWAttributeManager(attributes.AttributeManager): </span><span class="lines">@@ -427,7 +421,7 @@ </span><span class="cx"> class UOWDependencyProcessor(object): </span><span class="cx"> """in between the saving and deleting of objects, process "dependent" data, such as filling in </span><span class="cx"> a foreign key on a child item from a new primary key, or deleting association rows before a </span><del>- delete.""" </del><ins>+ delete. This object acts as a proxy to a DependencyProcessor.""" </ins><span class="cx"> def __init__(self, processor, targettask, isdeletefrom): </span><span class="cx"> self.processor = processor </span><span class="cx"> self.targettask = targettask </span></span></pre></div> <a id="sqlalchemybranchesschematestmasscreatepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/masscreate.py (1317 => 1318)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/masscreate.py 2006-04-22 19:01:37 UTC (rev 1317) +++ sqlalchemy/branches/schema/test/masscreate.py 2006-04-22 20:21:50 UTC (rev 1318) </span><span class="lines">@@ -16,7 +16,7 @@ </span><span class="cx"> if manage_attributes: </span><span class="cx"> attr_manager.register_attribute(User, 'id', uselist=False) </span><span class="cx"> attr_manager.register_attribute(User, 'name', uselist=False) </span><del>- attr_manager.register_attribute(User, 'addresses', uselist=True) </del><ins>+ attr_manager.register_attribute(User, 'addresses', uselist=True, trackparent=True) </ins><span class="cx"> attr_manager.register_attribute(Address, 'email', uselist=False) </span><span class="cx"> </span><span class="cx"> now = time.time() </span><span class="lines">@@ -35,7 +35,7 @@ </span><span class="cx"> a.email = 'fo...@ba...' </span><span class="cx"> u.addresses.append(a) </span><span class="cx"> # gc.collect() </span><del>- print len(managed_attributes) </del><ins>+# print len(managed_attributes) </ins><span class="cx"> # managed_attributes.clear() </span><span class="cx"> total = time.time() - now </span><span class="cx"> print "Total time", total </span></span></pre></div> <a id="sqlalchemybranchesschematestobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/objectstore.py (1317 => 1318)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/objectstore.py 2006-04-22 19:01:37 UTC (rev 1317) +++ sqlalchemy/branches/schema/test/objectstore.py 2006-04-22 20:21:50 UTC (rev 1318) </span><span class="lines">@@ -253,8 +253,7 @@ </span><span class="cx"> class B(object):pass </span><span class="cx"> </span><span class="cx"> assign_mapper(B,b_table) </span><del>- assign_mapper(A,a_table,properties= {'bs' : relation - (B.mapper,private=True)}) </del><ins>+ assign_mapper(A,a_table,properties= {'bs' : relation(B.mapper,private=True)}) </ins><span class="cx"> </span><span class="cx"> # create some objects </span><span class="cx"> a = A(data='a1') </span><span class="lines">@@ -539,6 +538,8 @@ </span><span class="cx"> objectstore.get_session().flush() </span><span class="cx"> </span><span class="cx"> def testchildmove(self): </span><ins>+ """tests moving a child from one parent to the other, then deleting the first parent, properly + updates the child with the new parent. this tests the 'trackparent' option in the attributes module.""" </ins><span class="cx"> m = mapper(User, users, properties = dict( </span><span class="cx"> addresses = relation(mapper(Address, addresses), lazy = True, private = False) </span><span class="cx"> )) </span><span class="lines">@@ -557,8 +558,7 @@ </span><span class="cx"> objectstore.clear() </span><s... [truncated message content] |
From: <co...@sq...> - 2006-04-22 19:01:51
|
<!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>[1317] sqlalchemy/branches/schema/test: some more dev....going to need to track child objects to parents</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1317</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-22 14:01:37 -0500 (Sat, 22 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>some more dev....going to need to track child objects to parents</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingobjectstorepy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingpropertiespy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py</a></li> <li><a href="#sqlalchemybranchesschematestmanytomanypy">sqlalchemy/branches/schema/test/manytomany.py</a></li> <li><a href="#sqlalchemybranchesschematestobjectstorepy">sqlalchemy/branches/schema/test/objectstore.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemymappingmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py (1316 => 1317)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py 2006-04-22 17:36:30 UTC (rev 1316) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py 2006-04-22 19:01:37 UTC (rev 1317) </span><span class="lines">@@ -694,8 +694,9 @@ </span><span class="cx"> def cascade_iterator(self, type, object, recursive=None): </span><span class="cx"> if recursive is None: </span><span class="cx"> recursive=sets.Set() </span><del>- recursive.add(object) - yield object </del><ins>+ if object not in recursive: + recursive.add(object) + yield object </ins><span class="cx"> for prop in self.props.values(): </span><span class="cx"> for c in prop.cascade_iterator(type, object, recursive): </span><span class="cx"> yield c </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py (1316 => 1317)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py 2006-04-22 17:36:30 UTC (rev 1316) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py 2006-04-22 19:01:37 UTC (rev 1317) </span><span class="lines">@@ -338,6 +338,7 @@ </span><span class="cx"> the keyword argument 'entity_name' can also be provided which will be used by the import.""" </span><span class="cx"> for o in obj: </span><span class="cx"> for c in object_mapper(o, **kwargs).cascade_iterator('delete', o): </span><ins>+ print "CASCADING DELETE TO", c </ins><span class="cx"> if not self._is_bound(c): </span><span class="cx"> c = self.import_(c, **kwargs) </span><span class="cx"> self.uow.register_deleted(c) </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py (1316 => 1317)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py 2006-04-22 17:36:30 UTC (rev 1316) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py 2006-04-22 19:01:37 UTC (rev 1317) </span><span class="lines">@@ -145,7 +145,7 @@ </span><span class="cx"> if private: </span><span class="cx"> self.cascade = mapperutil.CascadeOptions("save-update, delete-orphan, delete") </span><span class="cx"> else: </span><del>- self.cascade = mapperutil.CascadeOptions("save-update") </del><ins>+ self.cascade = mapperutil.CascadeOptions() </ins><span class="cx"> </span><span class="cx"> self.association = association </span><span class="cx"> if selectalias is not None: </span><span class="lines">@@ -174,12 +174,13 @@ </span><span class="cx"> childlist = objectstore.global_attributes.get_history(object, self.key, passive = False) </span><span class="cx"> else: </span><span class="cx"> childlist = objectstore.global_attributes.get_history(object, self.key) </span><ins>+ </ins><span class="cx"> for c in childlist.added_items() + childlist.deleted_items() + childlist.unchanged_items(): </span><del>- if c is not None and c not in recursive: - recursive.add(c) - yield c - for c2 in self.mapper.cascade_iterator(type, c, recursive): - if c2 not in recursive: </del><ins>+ if c is not None: + if c not in recursive: + recursive.add(c) + yield c + for c2 in self.mapper.cascade_iterator(type, c, recursive): </ins><span class="cx"> yield c2 </span><span class="cx"> </span><span class="cx"> def copy(self): </span><span class="lines">@@ -498,18 +499,23 @@ </span><span class="cx"> # the child objects have to have their foreign key to the parent set to NULL </span><span class="cx"> if self.private and not self.post_update: </span><span class="cx"> for obj in deplist: </span><ins>+ print "HI ON", obj </ins><span class="cx"> childlist = getlist(obj, False) </span><span class="cx"> for child in childlist.deleted_items() + childlist.unchanged_items(): </span><span class="cx"> if child is None: </span><span class="cx"> continue </span><span class="cx"> # if private child object, and is in the uow's "deleted" list, </span><span class="cx"> # insure its in the list of items to be deleted </span><ins>+ print "DEL CHILD", child </ins><span class="cx"> if child in uowcommit.uow.deleted: </span><ins>+ print "REGISTER", child </ins><span class="cx"> uowcommit.register_object(child, isdelete=True) </span><span class="cx"> else: </span><span class="cx"> for obj in deplist: </span><ins>+ print "HI 2 ON", obj </ins><span class="cx"> childlist = getlist(obj, False) </span><span class="cx"> for child in childlist.deleted_items() + childlist.unchanged_items(): </span><ins>+ print "DELCHILD", child </ins><span class="cx"> if child is not None: </span><span class="cx"> self._synchronize(obj, child, None, True) </span><span class="cx"> uowcommit.register_object(child, postupdate=self.post_update) </span><span class="lines">@@ -554,6 +560,7 @@ </span><span class="cx"> childlist = getlist(obj, passive=True) </span><span class="cx"> if childlist is not None: </span><span class="cx"> for child in childlist.added_items(): </span><ins>+ print "ADDCHILD", child </ins><span class="cx"> self._synchronize(obj, child, None, False) </span><span class="cx"> if self.direction == PropertyLoader.ONETOMANY and child is not None: </span><span class="cx"> uowcommit.register_object(child, postupdate=self.post_update) </span></span></pre></div> <a id="sqlalchemybranchesschematestmanytomanypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/manytomany.py (1316 => 1317)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/manytomany.py 2006-04-22 17:36:30 UTC (rev 1316) +++ sqlalchemy/branches/schema/test/manytomany.py 2006-04-22 19:01:37 UTC (rev 1317) </span><span class="lines">@@ -63,7 +63,7 @@ </span><span class="cx"> ) </span><span class="cx"> </span><span class="cx"> global place_place </span><del>- place_place = Table('place_place', db, </del><ins>+ place_place = Table('place_place', metadata, </ins><span class="cx"> Column('pl1_id', Integer, ForeignKey('place.place_id')), </span><span class="cx"> Column('pl2_id', Integer, ForeignKey('place.place_id')), </span><span class="cx"> ) </span></span></pre></div> <a id="sqlalchemybranchesschematestobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/objectstore.py (1316 => 1317)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/objectstore.py 2006-04-22 17:36:30 UTC (rev 1316) +++ sqlalchemy/branches/schema/test/objectstore.py 2006-04-22 19:01:37 UTC (rev 1317) </span><span class="lines">@@ -538,6 +538,27 @@ </span><span class="cx"> u.address.email_address = 'im...@fo...' </span><span class="cx"> objectstore.get_session().flush() </span><span class="cx"> </span><ins>+ def testchildmove(self): + m = mapper(User, users, properties = dict( + addresses = relation(mapper(Address, addresses), lazy = True, private = False) + )) + u1 = User() + u1.user_name = 'user1' + u2 = User() + u2.user_name = 'user2' + a = Address() + a.email_address = 'address1' + u1.addresses.append(a) + objectstore.flush() + del u1.addresses[0] + u2.addresses.append(a) + objectstore.delete(u1) + objectstore.flush() + objectstore.clear() + u2 = m.get(u2.user_id) + assert len(u2.addresses) == 1 + + </ins><span class="cx"> def testdelete(self): </span><span class="cx"> m = mapper(User, users, properties = dict( </span><span class="cx"> address = relation(mapper(Address, addresses), lazy = True, uselist = False, private = False) </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-22 17:36:49
|
<!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>[1316] sqlalchemy/branches/schema/test: dev</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1316</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-22 12:36:30 -0500 (Sat, 22 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>dev</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyattributespy">sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyenginestrategiespy">sqlalchemy/branches/schema/lib/sqlalchemy/engine/strategies.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingobjectstorepy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingpropertiespy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingsyncpy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/sync.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingunitofworkpy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingutilpy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/util.py</a></li> <li><a href="#sqlalchemybranchesschematestobjectstorepy">sqlalchemy/branches/schema/test/objectstore.py</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyenginethreadlocalpy">sqlalchemy/branches/schema/lib/sqlalchemy/engine/threadlocal.py</a></li> </ul> <h3>Removed Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyenginetransactionalpy">sqlalchemy/branches/schema/lib/sqlalchemy/engine/transactional.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemyattributespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py (1315 => 1316)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py 2006-04-22 16:45:04 UTC (rev 1315) +++ sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py 2006-04-22 17:36:30 UTC (rev 1316) </span><span class="lines">@@ -353,12 +353,10 @@ </span><span class="cx"> def set_attribute(self, obj, key, value, **kwargs): </span><span class="cx"> """sets the value of an object's attribute.""" </span><span class="cx"> self.get_unexec_history(obj, key).setattr(value, **kwargs) </span><del>- self.value_changed(obj, key, value) </del><span class="cx"> </span><span class="cx"> def delete_attribute(self, obj, key, **kwargs): </span><span class="cx"> """deletes the value from an object's attribute.""" </span><span class="cx"> self.get_unexec_history(obj, key).delattr(**kwargs) </span><del>- self.value_changed(obj, key, None) </del><span class="cx"> </span><span class="cx"> def rollback(self, *obj): </span><span class="cx"> """rolls back all attribute changes on the given list of objects, </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyenginestrategiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/engine/strategies.py (1315 => 1316)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/strategies.py 2006-04-22 16:45:04 UTC (rev 1315) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/strategies.py 2006-04-22 17:36:30 UTC (rev 1316) </span><span class="lines">@@ -7,7 +7,7 @@ </span><span class="cx"> import re </span><span class="cx"> import cgi </span><span class="cx"> </span><del>-from sqlalchemy.engine import base, default, transactional </del><ins>+from sqlalchemy.engine import base, default, threadlocal </ins><span class="cx"> </span><span class="cx"> strategies = {} </span><span class="cx"> </span><span class="lines">@@ -57,9 +57,9 @@ </span><span class="cx"> if poolclass is not None: </span><span class="cx"> poolargs.setdefault('poolclass', poolclass) </span><span class="cx"> poolargs['use_threadlocal'] = True </span><del>- provider = transactional.TLocalConnectionProvider(dialect, opts, **poolargs) </del><ins>+ provider = threadlocal.TLocalConnectionProvider(dialect, opts, **poolargs) </ins><span class="cx"> </span><del>- return transactional.TLEngine(provider, dialect, **kwargs) </del><ins>+ return threadlocal.TLEngine(provider, dialect, **kwargs) </ins><span class="cx"> ThreadLocalEngineStrategy() </span><span class="cx"> </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyenginethreadlocalpyfromrev1315sqlalchemybranchesschemalibsqlalchemyenginetransactionalpy"></a> <div class="copfile"><h4>Copied: sqlalchemy/branches/schema/lib/sqlalchemy/engine/threadlocal.py (from rev 1315, sqlalchemy/branches/schema/lib/sqlalchemy/engine/transactional.py) (1315 => 1316)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/transactional.py 2006-04-22 16:45:04 UTC (rev 1315) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/threadlocal.py 2006-04-22 17:36:30 UTC (rev 1316) </span><span class="lines">@@ -0,0 +1,84 @@ </span><ins>+from sqlalchemy import schema, exceptions, util, sql, types +import StringIO, sys, re +import base, default + +"""provides a thread-local transactional wrapper around the basic ComposedSQLEngine. multiple calls to engine.connect() +will return the same connection for the same thread. also provides begin/commit methods on the engine itself +which correspond to a thread-local transaction.""" + +class TLTransaction(base.Transaction): + def rollback(self): + try: + base.Transaction.rollback(self) + finally: + try: + del self.connection.engine.context.transaction + except AttributeError: + pass + def commit(self): + try: + base.Transaction.commit(self) + stack = self.connection.engine.context.transaction + stack.pop() + if len(stack) == 0: + del self.connection.engine.context.transaction + except: + try: + del self.connection.engine.context.transaction + except AttributeError: + pass + raise + +class TLConnection(base.Connection): + def _create_transaction(self, parent): + return TLTransaction(self, parent) + def begin(self): + t = base.Connection.begin(self) + if not hasattr(self.engine.context, 'transaction'): + self.engine.context.transaction = [] + self.engine.context.transaction.append(t) + return t + +class TLEngine(base.ComposedSQLEngine): + """a ComposedSQLEngine that includes support for thread-local managed transactions. This engine + is better suited to be used with threadlocal Pool object.""" + def __init__(self, *args, **kwargs): + """the TLEngine relies upon the ConnectionProvider having "threadlocal" behavior, + so that once a connection is checked out for the current thread, you get that same connection + repeatedly.""" + base.ComposedSQLEngine.__init__(self, *args, **kwargs) + self.context = util.ThreadLocal() + def raw_connection(self): + """returns a DBAPI connection.""" + return self.connection_provider.get_connection() + def connect(self, **kwargs): + """returns a Connection that is not thread-locally scoped. this is the equilvalent to calling + "connect()" on a ComposedSQLEngine.""" + return base.Connection(self, self.connection_provider.unique_connection()) + def contextual_connect(self, **kwargs): + """returns a TLConnection which is thread-locally scoped.""" + return TLConnection(self, **kwargs) + def begin(self): + return self.connect().begin() + def commit(self): + if hasattr(self.context, 'transaction'): + self.context.transaction[-1].commit() + def rollback(self): + if hasattr(self.context, 'transaction'): + self.context.transaction[-1].rollback() + def transaction(self, func, *args, **kwargs): + """executes the given function within a transaction boundary. this is a shortcut for + explicitly calling begin() and commit() and optionally rollback() when execptions are raised. + The given *args and **kwargs will be passed to the function as well, which could be handy + in constructing decorators.""" + trans = self.begin() + try: + func(*args, **kwargs) + except: + trans.rollback() + raise + trans.commit() + +class TLocalConnectionProvider(default.PoolConnectionProvider): + def unique_connection(self): + return self._pool.unique_connection() </ins></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyenginetransactionalpy"></a> <div class="delfile"><h4>Deleted: sqlalchemy/branches/schema/lib/sqlalchemy/engine/transactional.py (1315 => 1316)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/transactional.py 2006-04-22 16:45:04 UTC (rev 1315) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/transactional.py 2006-04-22 17:36:30 UTC (rev 1316) </span><span class="lines">@@ -1,84 +0,0 @@ </span><del>-from sqlalchemy import schema, exceptions, util, sql, types -import StringIO, sys, re -import base, default - -"""provides a thread-local transactional wrapper around the basic ComposedSQLEngine. multiple calls to engine.connect() -will return the same connection for the same thread. also provides begin/commit methods on the engine itself -which correspond to a thread-local transaction.""" - -class TLTransaction(base.Transaction): - def rollback(self): - try: - base.Transaction.rollback(self) - finally: - try: - del self.connection.engine.context.transaction - except AttributeError: - pass - def commit(self): - try: - base.Transaction.commit(self) - stack = self.connection.engine.context.transaction - stack.pop() - if len(stack) == 0: - del self.connection.engine.context.transaction - except: - try: - del self.connection.engine.context.transaction - except AttributeError: - pass - raise - -class TLConnection(base.Connection): - def _create_transaction(self, parent): - return TLTransaction(self, parent) - def begin(self): - t = base.Connection.begin(self) - if not hasattr(self.engine.context, 'transaction'): - self.engine.context.transaction = [] - self.engine.context.transaction.append(t) - return t - -class TLEngine(base.ComposedSQLEngine): - """a ComposedSQLEngine that includes support for thread-local managed transactions. This engine - is better suited to be used with threadlocal Pool object.""" - def __init__(self, *args, **kwargs): - """the TLEngine relies upon the ConnectionProvider having "threadlocal" behavior, - so that once a connection is checked out for the current thread, you get that same connection - repeatedly.""" - base.ComposedSQLEngine.__init__(self, *args, **kwargs) - self.context = util.ThreadLocal() - def raw_connection(self): - """returns a DBAPI connection.""" - return self.connection_provider.unique_connection() - def connect(self, **kwargs): - """returns a Connection that is not thread-locally scoped. this is the equilvalent to calling - "connect()" on a ComposedSQLEngine.""" - return base.Connection(self, self.connection_provider.unique_connection()) - def contextual_connect(self, **kwargs): - """returns a TLConnection which is thread-locally scoped.""" - return TLConnection(self, **kwargs) - def begin(self): - return self.connect().begin() - def commit(self): - if hasattr(self.context, 'transaction'): - self.context.transaction[-1].commit() - def rollback(self): - if hasattr(self.context, 'transaction'): - self.context.transaction[-1].rollback() - def transaction(self, func, *args, **kwargs): - """executes the given function within a transaction boundary. this is a shortcut for - explicitly calling begin() and commit() and optionally rollback() when execptions are raised. - The given *args and **kwargs will be passed to the function as well, which could be handy - in constructing decorators.""" - trans = self.begin() - try: - func(*args, **kwargs) - except: - trans.rollback() - raise - trans.commit() - -class TLocalConnectionProvider(default.PoolConnectionProvider): - def unique_connection(self): - return self._pool.unique_connection() </del></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py (1315 => 1316)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py 2006-04-22 16:45:04 UTC (rev 1315) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py 2006-04-22 17:36:30 UTC (rev 1316) </span><span class="lines">@@ -15,6 +15,7 @@ </span><span class="cx"> import objectstore </span><span class="cx"> import sys </span><span class="cx"> import weakref </span><ins>+import sets </ins><span class="cx"> </span><span class="cx"> # a dictionary mapping classes to their primary mappers </span><span class="cx"> mapper_registry = weakref.WeakKeyDictionary() </span><span class="lines">@@ -690,17 +691,15 @@ </span><span class="cx"> if self.inherits is not None: </span><span class="cx"> uowcommit.register_dependency(self.inherits, self) </span><span class="cx"> </span><del>- def cascade_iterator(self, type, object): </del><ins>+ def cascade_iterator(self, type, object, recursive=None): + if recursive is None: + recursive=sets.Set() + recursive.add(object) </ins><span class="cx"> yield object </span><span class="cx"> for prop in self.props.values(): </span><del>- for c in prop.cascade_iterator(type, object): </del><ins>+ for c in prop.cascade_iterator(type, object, recursive): </ins><span class="cx"> yield c </span><span class="cx"> </span><del>-# def register_deleted(self, obj, uow): -# for prop in self.props.values(): -# prop.register_deleted(obj, uow) - - </del><span class="cx"> def _identity_key(self, row): </span><span class="cx"> return objectstore.get_row_key(row, self.class_, self.pks_by_table[self.table], self.entity_name) </span><span class="cx"> </span><span class="lines">@@ -787,7 +786,7 @@ </span><span class="cx"> """called when the mapper receives a row. instance is the parent instance </span><span class="cx"> corresponding to the row. """ </span><span class="cx"> raise NotImplementedError() </span><del>- def cascade_iterator(self, type, object): </del><ins>+ def cascade_iterator(self, type, object, recursive=None): </ins><span class="cx"> return [] </span><span class="cx"> def copy(self): </span><span class="cx"> raise NotImplementedError() </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py (1315 => 1316)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py 2006-04-22 16:45:04 UTC (rev 1315) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py 2006-04-22 17:36:30 UTC (rev 1316) </span><span class="lines">@@ -28,7 +28,7 @@ </span><span class="cx"> def add(self, connection_or_engine): </span><span class="cx"> if self.connections.has_key(connection_or_engine): </span><span class="cx"> return self.connections[connection_or_engine][0] </span><del>- c = connection_or_engine.connect() </del><ins>+ c = connection_or_engine.contextual_connect() </ins><span class="cx"> e = c.engine </span><span class="cx"> if not self.connections.has_key(e): </span><span class="cx"> self.connections[e] = (c, c.begin()) </span><span class="lines">@@ -91,20 +91,21 @@ </span><span class="cx"> method which performs select operations for Mapper and Query. </span><span class="cx"> if this Session is transactional, </span><span class="cx"> the connection will be in the context of this session's transaction. otherwise, the connection </span><del>- will be unique. </del><ins>+ is returned by the contextual_connect method, which some Engines override to return a thread-local + connection, and will have close_with_result set to True. </ins><span class="cx"> </span><del>- the given **kwargs will be sent to the engine's connect() method, if no transaction is in progress.""" </del><ins>+ the given **kwargs will be sent to the engine's contextual_connect() method, if no transaction is in progress.""" </ins><span class="cx"> if self.transaction is not None: </span><span class="cx"> return self.transaction.connection(mapper) </span><span class="cx"> else: </span><del>- return self.connect(mapper, **kwargs) </del><ins>+ return self.get_bind(mapper).contextual_connect(**kwargs) </ins><span class="cx"> def execute(self, mapper, clause, params, **kwargs): </span><span class="cx"> """using the given mapper to identify the appropriate Engine or Connection to be used for statement execution, </span><span class="cx"> executes the given ClauseElement using the provided parameter dictionary. Returns a ResultProxy corresponding </span><span class="cx"> to the execution's results. If this method allocates a new Connection for the operation, then the ResultProxy's close() </span><span class="cx"> method will release the resources of the underlying Connection, otherwise its a no-op. </span><span class="cx"> """ </span><del>- return self.connection(mapper, close_with_result=True).execute(clause, params, **kwargs) </del><ins>+ return self.connection(mapper).execute(clause, params, **kwargs) </ins><span class="cx"> def close(self): </span><span class="cx"> """closes this Session. </span><span class="cx"> </span><span class="lines">@@ -207,7 +208,11 @@ </span><span class="cx"> """flushes all the object modifications present in this session to the database. if object </span><span class="cx"> arguments are given, then only those objects (and immediate dependencies) are flushed.""" </span><span class="cx"> self.uow.flush(self, *obj) </span><del>- </del><ins>+ + def load(self, class_, *ident): + """given a class and a primary key identifier, loads the corresponding object.""" + return self.query(class_).get(*ident) + </ins><span class="cx"> def refresh(self, *obj): </span><span class="cx"> """reloads the attributes for the given objects from the database, clears </span><span class="cx"> any changes made.""" </span><span class="lines">@@ -267,7 +272,7 @@ </span><span class="cx"> self._register_new(object) </span><span class="cx"> </span><span class="cx"> def _update_impl(self, object, **kwargs): </span><del>- if self._is_bound(object): </del><ins>+ if self._is_bound(object) and object not in self.deleted: </ins><span class="cx"> return </span><span class="cx"> if not hasattr(object, '_instance_key'): </span><span class="cx"> raise InvalidRequestError("Instance '%s' is not persisted" % repr(object)) </span><span class="lines">@@ -291,7 +296,18 @@ </span><span class="cx"> def _bind_to(self, obj): </span><span class="cx"> """given an object, binds it to this session. changes on the object will affect </span><span class="cx"> the currently scoped UnitOfWork maintained by this session.""" </span><del>- obj._sa_session_id = self.hash_key </del><ins>+ if getattr(obj, '_sa_session_id', None) != self.hash_key: + old = getattr(obj, '_sa_session_id', None) + # remove from old session. we do this gingerly since _sessions is a WeakValueDict + # and it might be affected by other threads + if old is not None: + try: + sess = _sessions[old] + except KeyError: + sess = None + if sess is not None: + sess.expunge(old) + obj._sa_session_id = self.hash_key </ins><span class="cx"> def _is_bound(self, obj): </span><span class="cx"> return getattr(obj, '_sa_session_id', None) == self.hash_key </span><span class="cx"> def __contains__(self, obj): </span><span class="lines">@@ -307,7 +323,6 @@ </span><span class="cx"> dirty = property(lambda s:s.uow.dirty) </span><span class="cx"> deleted = property(lambda s:s.uow.deleted) </span><span class="cx"> new = property(lambda s:s.uow.new) </span><del>- modified_lists = property(lambda s:s.uow.modified_lists) </del><span class="cx"> identity_map = property(lambda s:s.uow.identity_map) </span><span class="cx"> </span><span class="cx"> def clear(self): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py (1315 => 1316)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py 2006-04-22 16:45:04 UTC (rev 1315) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py 2006-04-22 17:36:30 UTC (rev 1316) </span><span class="lines">@@ -17,6 +17,7 @@ </span><span class="cx"> import sync </span><span class="cx"> import mapper </span><span class="cx"> import objectstore </span><ins>+import util as mapperutil </ins><span class="cx"> from sqlalchemy.exceptions import * </span><span class="cx"> import sets </span><span class="cx"> </span><span class="lines">@@ -138,15 +139,13 @@ </span><span class="cx"> else: </span><span class="cx"> self.foreigntable = None </span><span class="cx"> </span><del>- # hibernate cascades: - # create, merge, save-update, delete, lock, refresh, evict, replicate. </del><span class="cx"> if cascade is not None: </span><del>- self.cascade = sets.Set([c.strip() for c in cascade.split(',')]) </del><ins>+ self.cascade = mapperutil.CascadeOptions(cascade) </ins><span class="cx"> else: </span><span class="cx"> if private: </span><del>- self.cascade = sets.Set(["delete-orphan", "delete"]) </del><ins>+ self.cascade = mapperutil.CascadeOptions("save-update, delete-orphan, delete") </ins><span class="cx"> else: </span><del>- self.cascade = sets.Set() </del><ins>+ self.cascade = mapperutil.CascadeOptions("save-update") </ins><span class="cx"> </span><span class="cx"> self.association = association </span><span class="cx"> if selectalias is not None: </span><span class="lines">@@ -161,22 +160,27 @@ </span><span class="cx"> self.backref = backref </span><span class="cx"> self.is_backref = is_backref </span><span class="cx"> </span><del>- private = property(lambda s:"delete-orphan" in s.cascade) </del><ins>+ private = property(lambda s:s.cascade.delete_orphan) </ins><span class="cx"> </span><del>- def cascade_iterator(self, type, object): </del><ins>+ def cascade_iterator(self, type, object, recursive=None): </ins><span class="cx"> </span><span class="cx"> if not type in self.cascade: </span><span class="cx"> return </span><span class="cx"> </span><ins>+ if recursive is None: + recursive = sets.Set() + </ins><span class="cx"> if self.uselist: </span><span class="cx"> childlist = objectstore.global_attributes.get_history(object, self.key, passive = False) </span><span class="cx"> else: </span><span class="cx"> childlist = objectstore.global_attributes.get_history(object, self.key) </span><span class="cx"> for c in childlist.added_items() + childlist.deleted_items() + childlist.unchanged_items(): </span><del>- if c is not None: </del><ins>+ if c is not None and c not in recursive: + recursive.add(c) </ins><span class="cx"> yield c </span><del>- for c2 in self.mapper.cascade_iterator(type, c): - yield c2 </del><ins>+ for c2 in self.mapper.cascade_iterator(type, c, recursive): + if c2 not in recursive: + yield c2 </ins><span class="cx"> </span><span class="cx"> def copy(self): </span><span class="cx"> x = self.__class__.__new__(self.__class__) </span><span class="lines">@@ -330,6 +334,7 @@ </span><span class="cx"> return None </span><span class="cx"> </span><span class="cx"> </span><ins>+ # TODO: this should be moved to an external object </ins><span class="cx"> class MapperStub(object): </span><span class="cx"> """poses as a Mapper representing the association table in a many-to-many </span><span class="cx"> join, when performing a commit(). </span><span class="lines">@@ -345,7 +350,8 @@ </span><span class="cx"> pass </span><span class="cx"> def _primary_mapper(self): </span><span class="cx"> return self </span><del>- </del><ins>+ + # TODO: this method should be moved to an external object </ins><span class="cx"> def register_dependencies(self, uowcommit): </span><span class="cx"> """tells a UOWTransaction what mappers are dependent on which, with regards </span><span class="cx"> to the two or three mappers handled by this PropertyLoader. </span><span class="lines">@@ -409,9 +415,11 @@ </span><span class="cx"> else: </span><span class="cx"> raise AssertionError(" no foreign key ?") </span><span class="cx"> </span><ins>+ # TODO: this method should be moved to an external object </ins><span class="cx"> def get_object_dependencies(self, obj, uowcommit, passive = True): </span><span class="cx"> return uowcommit.uow.attributes.get_history(obj, self.key, passive = passive) </span><span class="cx"> </span><ins>+ # TODO: this method should be moved to an external object </ins><span class="cx"> def whose_dependent_on_who(self, obj1, obj2): </span><span class="cx"> """given an object pair assuming obj2 is a child of obj1, returns a tuple </span><span class="cx"> with the dependent object second, or None if they are equal. </span><span class="lines">@@ -424,6 +432,7 @@ </span><span class="cx"> else: </span><span class="cx"> return (obj2, obj1) </span><span class="cx"> </span><ins>+ # TODO: this method should be moved to an external object </ins><span class="cx"> def process_dependencies(self, task, deplist, uowcommit, delete = False): </span><span class="cx"> """this method is called during a commit operation to synchronize data between a parent and child object. </span><span class="cx"> it also can establish child or parent objects within the unit of work as "to be saved" or "deleted" </span><span class="lines">@@ -431,9 +440,7 @@ </span><span class="cx"> #print self.mapper.table.name + " " + self.key + " " + repr(len(deplist)) + " process_dep isdelete " + repr(delete) + " direction " + repr(self.direction) </span><span class="cx"> </span><span class="cx"> def getlist(obj, passive=True): </span><del>- l = self.get_object_dependencies(obj, uowcommit, passive) - uowcommit.register_saved_history(l) - return l </del><ins>+ return self.get_object_dependencies(obj, uowcommit, passive) </ins><span class="cx"> </span><span class="cx"> connection = uowcommit.transaction.connection(self.mapper) </span><span class="cx"> </span><span class="lines">@@ -564,6 +571,7 @@ </span><span class="cx"> #print "PLAIN PROPLOADER EXEC NON-PRIAMRY", repr(id(self)), repr(self.mapper.class_), self.key </span><span class="cx"> objectstore.global_attributes.create_history(instance, self.key, self.uselist, cascade=self.cascade) </span><span class="cx"> </span><ins>+ # TODO: this method should be moved to an external object </ins><span class="cx"> def _compile_synchronizers(self): </span><span class="cx"> """assembles a list of 'synchronization rules', which are instructions on how to populate </span><span class="cx"> the objects on each side of a relationship. This is done when a PropertyLoader is </span><span class="lines">@@ -584,6 +592,7 @@ </span><span class="cx"> else: </span><span class="cx"> self.syncrules.compile(self.primaryjoin, parent_tables, target_tables) </span><span class="cx"> </span><ins>+ # TODO: this method should be moved to an external object </ins><span class="cx"> def _synchronize(self, obj, child, associationrow, clearkeys): </span><span class="cx"> """called during a commit to execute the full list of syncrules on the </span><span class="cx"> given object/child/optional association row""" </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingsyncpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/sync.py (1315 => 1316)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/sync.py 2006-04-22 16:45:04 UTC (rev 1315) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/sync.py 2006-04-22 17:36:30 UTC (rev 1316) </span><span class="lines">@@ -10,7 +10,7 @@ </span><span class="cx"> import sqlalchemy.schema as schema </span><span class="cx"> from sqlalchemy.exceptions import * </span><span class="cx"> </span><del>-"""contains the ClauseSynchronizer class which is used to map attributes between two objects </del><ins>+"""contains the ClauseSynchronizer class, which is used to map attributes between two objects </ins><span class="cx"> in a manner corresponding to a SQL clause that compares column values.""" </span><span class="cx"> </span><span class="cx"> ONETOMANY = 0 </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingunitofworkpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py (1315 => 1316)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py 2006-04-22 16:45:04 UTC (rev 1315) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py 2006-04-22 17:36:30 UTC (rev 1316) </span><span class="lines">@@ -46,13 +46,13 @@ </span><span class="cx"> sess = get_session(obj, raiseerror=False) </span><span class="cx"> if sess is None: </span><span class="cx"> return </span><del>- sess.modified_lists.append(self) </del><ins>+ sess._register_dirty(obj) </ins><span class="cx"> if self.cascade is not None: </span><span class="cx"> if isdelete: </span><del>- if "delete-orphan" in self.cascade: </del><ins>+ if self.cascade.delete_orphan: </ins><span class="cx"> sess.delete(item) </span><span class="cx"> else: </span><del>- if "save-update" in self.cascade: </del><ins>+ if self.cascade.save_update: </ins><span class="cx"> sess.save_or_update(item) </span><span class="cx"> def append(self, item, _mapper_nohistory = False): </span><span class="cx"> if _mapper_nohistory: </span><span class="lines">@@ -68,13 +68,13 @@ </span><span class="cx"> obj = self.obj </span><span class="cx"> sess = get_session(obj, raiseerror=False) </span><span class="cx"> if sess is not None: </span><del>- sess.save_or_update(obj) </del><ins>+ sess._register_dirty(obj) </ins><span class="cx"> if self.cascade is not None: </span><del>- if "delete-orphan" in self.cascade: </del><ins>+ if oldvalue is not None and self.cascade.delete_orphan: </ins><span class="cx"> sess.delete(oldvalue) </span><del>- if "save-update" in self.cascade: </del><ins>+ if newvalue is not None and self.cascade.save_update: </ins><span class="cx"> sess.save_or_update(newvalue) </span><del>- </del><ins>+ </ins><span class="cx"> class UOWAttributeManager(attributes.AttributeManager): </span><span class="cx"> """overrides AttributeManager to provide unit-of-work "dirty" hooks when scalar attribues are modified, plus factory methods for UOWProperrty/UOWListElement.""" </span><span class="cx"> def __init__(self): </span><span class="lines">@@ -90,7 +90,7 @@ </span><span class="cx"> return UOWListElement(obj, key, list_, **kwargs) </span><span class="cx"> </span><span class="cx"> class UnitOfWork(object): </span><del>- """main UOW object which stores lists of dirty/new/deleted objects, as well as 'modified_lists' for list attributes. provides top-level "flush" functionality as well as the transaction boundaries with the SQLEngine(s) involved in a write operation.""" </del><ins>+ """main UOW object which stores lists of dirty/new/deleted objects. provides top-level "flush" functionality as well as the transaction boundaries with the SQLEngine(s) involved in a write operation.""" </ins><span class="cx"> def __init__(self, identity_map=None): </span><span class="cx"> if identity_map is not None: </span><span class="cx"> self.identity_map = identity_map </span><span class="lines">@@ -101,9 +101,6 @@ </span><span class="cx"> self.new = util.HashSet(ordered = True) </span><span class="cx"> self.dirty = util.HashSet() </span><span class="cx"> </span><del>- # guess what. we dont even need this anymore ! hooray - TODO: take it out - self.modified_lists = util.HashSet() - </del><span class="cx"> self.deleted = util.HashSet() </span><span class="cx"> </span><span class="cx"> def get(self, class_, *id): </span><span class="lines">@@ -198,12 +195,14 @@ </span><span class="cx"> raise InvalidRequestError("Object '%s' already has an identity - it cant be registered as new" % repr(obj)) </span><span class="cx"> if not self.new.contains(obj): </span><span class="cx"> self.new.append(obj) </span><ins>+ self.unregister_deleted(obj) </ins><span class="cx"> </span><span class="cx"> def register_dirty(self, obj): </span><span class="cx"> if not self.dirty.contains(obj): </span><span class="cx"> self._validate_obj(obj) </span><span class="cx"> self.dirty.append(obj) </span><del>- </del><ins>+ self.unregister_deleted(obj) + </ins><span class="cx"> def is_dirty(self, obj): </span><span class="cx"> if not self.dirty.contains(obj): </span><span class="cx"> return False </span><span class="lines">@@ -236,15 +235,6 @@ </span><span class="cx"> continue </span><span class="cx"> flush_context.register_object(obj) </span><span class="cx"> </span><del>- for item in self.modified_lists: - obj = item.obj - if objset is not None and not objset.contains(obj): - continue - if self.deleted.contains(obj): - continue - flush_context.register_object(obj, listonly = True) - flush_context.register_saved_history(item) - </del><span class="cx"> for obj in self.deleted: </span><span class="cx"> if objset is not None and not objset.contains(obj): </span><span class="cx"> continue </span><span class="lines">@@ -285,7 +275,6 @@ </span><span class="cx"> self.mappers = util.HashSet() </span><span class="cx"> self.dependencies = {} </span><span class="cx"> self.tasks = {} </span><del>- self.saved_histories = util.HashSet() </del><span class="cx"> self.__modified = False </span><span class="cx"> </span><span class="cx"> def register_object(self, obj, isdelete = False, listonly = False, postupdate=False, **kwargs): </span><span class="lines">@@ -362,9 +351,6 @@ </span><span class="cx"> task.dependencies.append(UOWDependencyProcessor(processor, targettask, isdeletefrom)) </span><span class="cx"> self.__modified = True </span><span class="cx"> </span><del>- def register_saved_history(self, listobj): - self.saved_histories.append(listobj) - </del><span class="cx"> def execute(self, echo=False): </span><span class="cx"> for task in self.tasks.values(): </span><span class="cx"> task.mapper.register_dependencies(self) </span><span class="lines">@@ -394,19 +380,7 @@ </span><span class="cx"> self.uow._remove_deleted(elem.obj) </span><span class="cx"> else: </span><span class="cx"> self.uow.register_clean(elem.obj) </span><del>- - for obj in self.saved_histories: - try: - obj.commit() - del self.uow.modified_lists[obj] - except KeyError: - pass </del><span class="cx"> </span><del>- # this assertion only applies to a full flush(), not a - # partial one - #if len(self.uow.new) > 0 or len(self.uow.dirty) >0 or len(self.uow.modified_lists) > 0: - # raise "assertion failed" - </del><span class="cx"> def _sort_dependencies(self): </span><span class="cx"> """creates a hierarchical tree of dependent tasks. the root node is returned. </span><span class="cx"> when the root node is executed, it also executes its child tasks recursively.""" </span><span class="lines">@@ -668,12 +642,7 @@ </span><span class="cx"> if not o in childtask.objects: </span><span class="cx"> # item needs to be saved since its added, or attached to a deleted object </span><span class="cx"> if isdelete: </span><del>- childtask.append(o, "delete" in processor.cascade) - #if isdelete and "delete" in processor.cascade: - # childstask.append(o, True) - #elif not isdelete and "save-update" in processor.cascade: - # childtask.append(o, False) - #childtask.append(o, isdelete=isdelete and dep.processor.private) </del><ins>+ childtask.append(o, processor.cascade.delete) </ins><span class="cx"> if cyclicaldep: </span><span class="cx"> # cyclical, so create a placeholder UOWTask that may be built into the </span><span class="cx"> # final task tree </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingutilpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/util.py (1315 => 1316)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/util.py 2006-04-22 16:45:04 UTC (rev 1315) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/util.py 2006-04-22 17:36:30 UTC (rev 1316) </span><span class="lines">@@ -6,6 +6,7 @@ </span><span class="cx"> </span><span class="cx"> </span><span class="cx"> import sqlalchemy.sql as sql </span><ins>+import sets </ins><span class="cx"> </span><span class="cx"> class TableFinder(sql.ClauseVisitor): </span><span class="cx"> """given a Clause, locates all the Tables within it into a list.""" </span><span class="lines">@@ -29,3 +30,17 @@ </span><span class="cx"> def visit_column(self, column): </span><span class="cx"> if self.check_columns: </span><span class="cx"> column.table.accept_visitor(self) </span><ins>+ + +class CascadeOptions(object): + """keeps track of the options sent to relation().cascade""" + def __init__(self, arg=""): + values = sets.Set([c.strip() for c in arg.split(',')]) + self.delete_orphan = "delete-orphan" in values + self.delete = "delete" in values or self.delete_orphan + self.save_update = "save-update" in values + self.merge = "merge" in values + self.expunge = "expunge" in values + def __contains__(self, item): + return getattr(self, item.replace("-", "_"), False) + </ins><span class="cx">\ No newline at end of file </span></span></pre></div> <a id="sqlalchemybranchesschematestobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/objectstore.py (1315 => 1316)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/objectstore.py 2006-04-22 16:45:04 UTC (rev 1315) +++ sqlalchemy/branches/schema/test/objectstore.py 2006-04-22 17:36:30 UTC (rev 1316) </span><span class="lines">@@ -62,7 +62,7 @@ </span><span class="cx"> </span><span class="cx"> u = User() </span><span class="cx"> a = Address() </span><del>- s.add(u, a) </del><ins>+ s.save(u, a) </ins><span class="cx"> a.user = u </span><span class="cx"> #print repr(a.__class__._attribute_manager.get_history(a, 'user').added_items()) </span><span class="cx"> #print repr(u.addresses.added_items()) </span><span class="lines">@@ -410,7 +410,6 @@ </span><span class="cx"> </span><span class="cx"> self.assert_(len(objectstore.get_session().new) == 0) </span><span class="cx"> self.assert_(len(objectstore.get_session().dirty) == 0) </span><del>- self.assert_(len(objectstore.get_session().modified_lists) == 0) </del><span class="cx"> </span><span class="cx"> def testbasic(self): </span><span class="cx"> # save two users </span><span class="lines">@@ -421,7 +420,7 @@ </span><span class="cx"> u2 = User() </span><span class="cx"> u2.user_name = 'savetester2' </span><span class="cx"> </span><del>- objectstore.get_session().add(u) </del><ins>+ objectstore.get_session().save(u) </ins><span class="cx"> </span><span class="cx"> objectstore.get_session().flush(u) </span><span class="cx"> objectstore.get_session().flush() </span><span class="lines">@@ -440,6 +439,7 @@ </span><span class="cx"> </span><span class="cx"> # change first users name and save </span><span class="cx"> u.user_name = 'modifiedname' </span><ins>+ assert u in objectstore.get_session().dirty </ins><span class="cx"> objectstore.get_session().flush() </span><span class="cx"> </span><span class="cx"> # select both </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-22 16:45:16
|
<!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>[1315] sqlalchemy/branches/schema/lib/sqlalchemy/engine: dev</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1315</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-22 11:45:04 -0500 (Sat, 22 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>dev</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyenginebasepy">sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyenginetransactionalpy">sqlalchemy/branches/schema/lib/sqlalchemy/engine/transactional.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemyenginebasepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py (1314 => 1315)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py 2006-04-21 23:18:17 UTC (rev 1314) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py 2006-04-22 16:45:04 UTC (rev 1315) </span><span class="lines">@@ -162,7 +162,11 @@ </span><span class="cx"> def _create_transaction(self, parent): </span><span class="cx"> return Transaction(self, parent) </span><span class="cx"> def connect(self): </span><ins>+ """connect() is implemented to return self so that an incoming Engine or Connection object can be treated similarly.""" </ins><span class="cx"> return self </span><ins>+ def contextual_connect(self): + """contextual_connect() is implemented to return self so that an incoming Engine or Connection object can be treated similarly.""" + return self </ins><span class="cx"> def begin(self): </span><span class="cx"> if self.transaction is None: </span><span class="cx"> self.transaction = self._create_transaction(None) </span><span class="lines">@@ -356,7 +360,7 @@ </span><span class="cx"> </span><span class="cx"> def _run_visitor(self, visitorcallable, element, connection=None, **kwargs): </span><span class="cx"> if connection is None: </span><del>- conn = self.connect() </del><ins>+ conn = self.contextual_connect() </ins><span class="cx"> else: </span><span class="cx"> conn = connection </span><span class="cx"> try: </span><span class="lines">@@ -366,23 +370,29 @@ </span><span class="cx"> conn.close() </span><span class="cx"> </span><span class="cx"> def execute(self, *args, **kwargs): </span><del>- connection = self.connect(close_with_result=True) </del><ins>+ connection = self.contextual_connect() </ins><span class="cx"> return connection.execute(*args, **kwargs) </span><span class="cx"> </span><span class="cx"> def execute_compiled(self, compiled, parameters, **kwargs): </span><del>- connection = self.connect(close_with_result=True) </del><ins>+ connection = self.contextual_connect() </ins><span class="cx"> return connection.execute_compiled(compiled, parameters, **kwargs) </span><span class="cx"> </span><span class="cx"> def compiler(self, statement, parameters, **kwargs): </span><span class="cx"> return self.dialect.compiler(statement, parameters, engine=self, **kwargs) </span><span class="cx"> </span><span class="cx"> def connect(self, **kwargs): </span><ins>+ """returns a newly allocated Connection object.""" </ins><span class="cx"> return Connection(self, **kwargs) </span><del>- </del><ins>+ + def contextual_connect(self, **kwargs): + """returns a Connection object which may be newly allocated, or may be part of some + ongoing context. This Connection is meant to be used by the various "auto-connecting" operations.""" + return Connection(self, close_with_result=True, **kwargs) + </ins><span class="cx"> def reflecttable(self, table, connection=None): </span><span class="cx"> """given a Table object, reflects its columns and properties from the database.""" </span><span class="cx"> if connection is None: </span><del>- conn = self.connect() </del><ins>+ conn = self.contextual_connect() </ins><span class="cx"> else: </span><span class="cx"> conn = connection </span><span class="cx"> try: </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyenginetransactionalpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/engine/transactional.py (1314 => 1315)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/transactional.py 2006-04-21 23:18:17 UTC (rev 1314) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/transactional.py 2006-04-22 16:45:04 UTC (rev 1315) </span><span class="lines">@@ -40,22 +40,23 @@ </span><span class="cx"> return t </span><span class="cx"> </span><span class="cx"> class TLEngine(base.ComposedSQLEngine): </span><del>- """a ComposedSQLEngine that includes support for thread-local managed transactions""" </del><ins>+ """a ComposedSQLEngine that includes support for thread-local managed transactions. This engine + is better suited to be used with threadlocal Pool object.""" </ins><span class="cx"> def __init__(self, *args, **kwargs): </span><span class="cx"> """the TLEngine relies upon the ConnectionProvider having "threadlocal" behavior, </span><span class="cx"> so that once a connection is checked out for the current thread, you get that same connection </span><span class="cx"> repeatedly.""" </span><span class="cx"> base.ComposedSQLEngine.__init__(self, *args, **kwargs) </span><span class="cx"> self.context = util.ThreadLocal() </span><del>- def unique_connection(self): </del><ins>+ def raw_connection(self): + """returns a DBAPI connection.""" + return self.connection_provider.unique_connection() + def connect(self, **kwargs): </ins><span class="cx"> """returns a Connection that is not thread-locally scoped. this is the equilvalent to calling </span><span class="cx"> "connect()" on a ComposedSQLEngine.""" </span><del>- return Connection(self, self.raw_unique_connection()) - def raw_unique_connection(self): - """returns a raw connection that is not thread-locally scoped. this is the equivalent to calling - raw_connection on a ComposedSQLEngine.""" - return self.connection_provider.unique_connection() - def connect(self, **kwargs): </del><ins>+ return base.Connection(self, self.connection_provider.unique_connection()) + def contextual_connect(self, **kwargs): + """returns a TLConnection which is thread-locally scoped.""" </ins><span class="cx"> return TLConnection(self, **kwargs) </span><span class="cx"> def begin(self): </span><span class="cx"> return self.connect().begin() </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-21 23:18: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>[1314] sqlalchemy/branches/schema/test: its ia little rough right now</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1314</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-21 18:18:17 -0500 (Fri, 21 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>its ia little rough right now</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyattributespy">sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingobjectstorepy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingunitofworkpy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py</a></li> <li><a href="#sqlalchemybranchesschematestrelationshipspy">sqlalchemy/branches/schema/test/relationships.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemyattributespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py (1313 => 1314)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py 2006-04-21 21:31:07 UTC (rev 1313) +++ sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py 2006-04-21 23:18:17 UTC (rev 1314) </span><span class="lines">@@ -82,6 +82,9 @@ </span><span class="cx"> #self.obj = obj </span><span class="cx"> self.key = key </span><span class="cx"> obj = property(lambda s:s.__obj()) </span><ins>+ def value_changed(self, *args, **kwargs): + self.obj._managed_value_changed = True + self.do_value_changed(*args, **kwargs) </ins><span class="cx"> def history(self, **kwargs): </span><span class="cx"> return self </span><span class="cx"> def plain_init(self, *args, **kwargs): </span><span class="lines">@@ -138,7 +141,7 @@ </span><span class="cx"> self.orig = ScalarAttribute.NONE </span><span class="cx"> def commit(self): </span><span class="cx"> self.orig = ScalarAttribute.NONE </span><del>- def value_changed(self, oldvalue, newvalue): </del><ins>+ def do_value_changed(self, oldvalue, newvalue): </ins><span class="cx"> pass </span><span class="cx"> def added_items(self): </span><span class="cx"> if self.orig is not ScalarAttribute.NONE: </span><span class="lines">@@ -181,7 +184,7 @@ </span><span class="cx"> obj.__dict__[key] = [] </span><span class="cx"> </span><span class="cx"> util.HistoryArraySet.__init__(self, list_, readonly=kwargs.get('readonly', False)) </span><del>- def value_changed(self, obj, key, item, listval, isdelete): </del><ins>+ def do_value_changed(self, obj, key, item, listval, isdelete): </ins><span class="cx"> pass </span><span class="cx"> def setattr(self, value, **kwargs): </span><span class="cx"> self.obj.__dict__[self.key] = value </span><span class="lines">@@ -312,7 +315,7 @@ </span><span class="cx"> def __init__(self): </span><span class="cx"> pass </span><span class="cx"> </span><del>- def value_changed(self, obj, key, value): </del><ins>+ def do_value_changed(self, obj, key, value): </ins><span class="cx"> """subclasses override this method to provide functionality that is triggered </span><span class="cx"> upon an attribute change of value.""" </span><span class="cx"> pass </span><span class="lines">@@ -367,6 +370,7 @@ </span><span class="cx"> hist.rollback() </span><span class="cx"> except KeyError: </span><span class="cx"> pass </span><ins>+ o._managed_value_changed = False </ins><span class="cx"> </span><span class="cx"> def commit(self, *obj): </span><span class="cx"> """commits all attribute changes on the given list of objects, </span><span class="lines">@@ -378,7 +382,11 @@ </span><span class="cx"> hist.commit() </span><span class="cx"> except KeyError: </span><span class="cx"> pass </span><del>- </del><ins>+ o._managed_value_changed = False + + def is_modified(self, object): + return getattr(object, '_managed_value_changed', False) + </ins><span class="cx"> def remove(self, obj): </span><span class="cx"> """called when an object is totally being removed from memory""" </span><span class="cx"> # currently a no-op since the state of the object is attached to the object itself </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py (1313 => 1314)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py 2006-04-21 21:31:07 UTC (rev 1313) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py 2006-04-21 23:18:17 UTC (rev 1314) </span><span class="lines">@@ -249,7 +249,7 @@ </span><span class="cx"> def save_or_update(self, *obj, **kwargs): </span><span class="cx"> for o in obj: </span><span class="cx"> for c in object_mapper(o, *kwargs).cascade_iterator('save-update', o): </span><del>- key = self._instance_key(c, raiseerror=False, **kwargs) </del><ins>+ key = getattr(o, '_instance_key', None) </ins><span class="cx"> if key is None: </span><span class="cx"> self._save_impl(c, **kwargs) </span><span class="cx"> else: </span><span class="lines">@@ -271,25 +271,11 @@ </span><span class="cx"> return </span><span class="cx"> if not hasattr(object, '_instance_key'): </span><span class="cx"> raise InvalidRequestError("Instance '%s' is not persisted" % repr(object)) </span><del>- self._register_dirty(object) - - def _instance_key(self, object, entity_name=None, raiseerror=True): - key = getattr(object, '_instance_key', None) - if key is None: - mapper = object_mapper(object, raiseerror=False) - if mapper is None: - mapper = class_mapper(object, entity_name=entity_name) - ident = mapper.identity(object) - for k in ident: - if k is None: - if raiseerror: - raise InvalidRequestError("Instance '%s' does not have a full set of identity values, and does not represent a saved entity in the database. Use the add() method to add unsaved instances to this Session." % str(object)) - else: - return None - key = mapper.identity_key(*ident) - return key </del><ins>+ if global_attributes.is_modified(object): + self._register_dirty(object) + else: + self._register_clean(object) </ins><span class="cx"> </span><del>- </del><span class="cx"> def _register_new(self, obj): </span><span class="cx"> self._bind_to(obj) </span><span class="cx"> self.uow.register_new(obj) </span><span class="lines">@@ -360,7 +346,19 @@ </span><span class="cx"> """ </span><span class="cx"> if instance is None: </span><span class="cx"> return None </span><del>- key = self._instance_key(instance, entity_name=entity_name, raiseerror=True) </del><ins>+ key = getattr(object, '_instance_key', None) + if key is None: + mapper = object_mapper(object, raiseerror=False) + if mapper is None: + mapper = class_mapper(object, entity_name=entity_name) + ident = mapper.identity(object) + for k in ident: + if k is None: + if raiseerror: + raise InvalidRequestError("Instance '%s' does not have a full set of identity values, and does not represent a saved entity in the database. Use the add() method to add unsaved instances to this Session." % str(object)) + else: + return None + key = mapper.identity_key(*ident) </ins><span class="cx"> u = self.uow </span><span class="cx"> if u.identity_map.has_key(key): </span><span class="cx"> return u.identity_map[key] </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingunitofworkpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py (1313 => 1314)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py 2006-04-21 21:31:07 UTC (rev 1313) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py 2006-04-21 23:18:17 UTC (rev 1314) </span><span class="lines">@@ -42,7 +42,7 @@ </span><span class="cx"> def __init__(self, obj, key, data=None, cascade=None, **kwargs): </span><span class="cx"> attributes.ListAttribute.__init__(self, obj, key, data=data, **kwargs) </span><span class="cx"> self.cascade = cascade </span><del>- def value_changed(self, obj, key, item, listval, isdelete): </del><ins>+ def do_value_changed(self, obj, key, item, listval, isdelete): </ins><span class="cx"> sess = get_session(obj, raiseerror=False) </span><span class="cx"> if sess is None: </span><span class="cx"> return </span><span class="lines">@@ -64,21 +64,16 @@ </span><span class="cx"> def __init__(self, obj, key, cascade=None, **kwargs): </span><span class="cx"> attributes.ScalarAttribute.__init__(self, obj, key, **kwargs) </span><span class="cx"> self.cascade=cascade </span><del>- def value_changed(self, oldvalue, newvalue): </del><ins>+ def do_value_changed(self, oldvalue, newvalue): </ins><span class="cx"> obj = self.obj </span><span class="cx"> sess = get_session(obj, raiseerror=False) </span><span class="cx"> if sess is not None: </span><del>- if hasattr(obj, '_instance_key'): - sess._register_dirty(obj) - else: - sess._register_new(obj) </del><ins>+ sess.save_or_update(obj) </ins><span class="cx"> if self.cascade is not None: </span><del>- if isdelete: - if "delete-orphan" in self.cascade: - sess.delete(item) - else: - if "save-update" in self.cascade: - sess.save_or_update(item) </del><ins>+ if "delete-orphan" in self.cascade: + sess.delete(oldvalue) + if "save-update" in self.cascade: + sess.save_or_update(newvalue) </ins><span class="cx"> </span><span class="cx"> class UOWAttributeManager(attributes.AttributeManager): </span><span class="cx"> """overrides AttributeManager to provide unit-of-work "dirty" hooks when scalar attribues are modified, plus factory methods for UOWProperrty/UOWListElement.""" </span><span class="lines">@@ -105,7 +100,10 @@ </span><span class="cx"> self.attributes = global_attributes </span><span class="cx"> self.new = util.HashSet(ordered = True) </span><span class="cx"> self.dirty = util.HashSet() </span><ins>+ + # guess what. we dont even need this anymore ! hooray - TODO: take it out </ins><span class="cx"> self.modified_lists = util.HashSet() </span><ins>+ </ins><span class="cx"> self.deleted = util.HashSet() </span><span class="cx"> </span><span class="cx"> def get(self, class_, *id): </span><span class="lines">@@ -247,11 +245,6 @@ </span><span class="cx"> flush_context.register_object(obj, listonly = True) </span><span class="cx"> flush_context.register_saved_history(item) </span><span class="cx"> </span><del>-# for o in item.added_items() + item.deleted_items(): -# if self.deleted.contains(o): -# continue -# flush_context.register_object(o, listonly=True) - </del><span class="cx"> for obj in self.deleted: </span><span class="cx"> if objset is not None and not objset.contains(obj): </span><span class="cx"> continue </span></span></pre></div> <a id="sqlalchemybranchesschematestrelationshipspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/relationships.py (1313 => 1314)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/relationships.py 2006-04-21 21:31:07 UTC (rev 1313) +++ sqlalchemy/branches/schema/test/relationships.py 2006-04-21 23:18:17 UTC (rev 1314) </span><span class="lines">@@ -74,12 +74,12 @@ </span><span class="cx"> global c </span><span class="cx"> a = A(); a.name = "a1" </span><span class="cx"> b = B(); b.name = "b1" </span><del>- session.add(a,b) </del><span class="cx"> c = C(); c.name = "c1"; c.a_row = a </span><span class="cx"> # we must have more than one d row or it won't fail </span><span class="cx"> d1 = D(); d1.name = "d1"; d1.b_row = b; d1.c_row = c </span><span class="cx"> d2 = D(); d2.name = "d2"; d2.b_row = b; d2.c_row = c </span><span class="cx"> d3 = D(); d3.name = "d3"; d3.b_row = b; d3.c_row = c </span><ins>+ session.save_or_update(a,b) </ins><span class="cx"> </span><span class="cx"> def tearDown(self): </span><span class="cx"> conn = session.connect() </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-21 21:31: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>[1313] sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py: tweak</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1313</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-21 16:31:07 -0500 (Fri, 21 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>tweak</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingobjectstorepy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemymappingobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py (1312 => 1313)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py 2006-04-21 21:26:54 UTC (rev 1312) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py 2006-04-21 21:31:07 UTC (rev 1313) </span><span class="lines">@@ -22,14 +22,17 @@ </span><span class="cx"> if self.parent is not None: </span><span class="cx"> return self.parent.connection(mapper) </span><span class="cx"> engine = self.session.get_bind(mapper) </span><del>- try: - return self.connections[engine][0] - except KeyError: - c = engine.connect() - self.connections[engine] = (c, c.begin()) - return c </del><ins>+ return self.add(engine) </ins><span class="cx"> def _begin(self): </span><span class="cx"> return SessionTransaction(self.session, self) </span><ins>+ def add(self, connection_or_engine): + if self.connections.has_key(connection_or_engine): + return self.connections[connection_or_engine][0] + c = connection_or_engine.connect() + e = c.engine + if not self.connections.has_key(e): + self.connections[e] = (c, c.begin()) + return self.connections[e][0] </ins><span class="cx"> def commit(self): </span><span class="cx"> if self.parent is not None: </span><span class="cx"> return </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-21 21:27:04
|
<!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>[1312] sqlalchemy/branches/schema/lib/sqlalchemy/mapping: more cascade...</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1312</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-21 16:26:54 -0500 (Fri, 21 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>more cascade...</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingpropertiespy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingunitofworkpy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemymappingpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py (1311 => 1312)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py 2006-04-21 21:22:18 UTC (rev 1311) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py 2006-04-21 21:26:54 UTC (rev 1312) </span><span class="lines">@@ -253,7 +253,7 @@ </span><span class="cx"> </span><span class="cx"> def _set_class_attribute(self, class_, key): </span><span class="cx"> """sets attribute behavior on our target class.""" </span><del>- objectstore.global_attributes.register_attribute(class_, key, uselist = self.uselist, deleteremoved = self.private, extension=self.attributeext, cascade=self.cascade) </del><ins>+ objectstore.global_attributes.register_attribute(class_, key, uselist = self.uselist, extension=self.attributeext, cascade=self.cascade) </ins><span class="cx"> </span><span class="cx"> def _get_direction(self): </span><span class="cx"> """determines our 'direction', i.e. do we represent one to many, many to many, etc.""" </span><span class="lines">@@ -612,7 +612,7 @@ </span><span class="cx"> def _set_class_attribute(self, class_, key): </span><span class="cx"> # establish a class-level lazy loader on our class </span><span class="cx"> #print "SETCLASSATTR LAZY", repr(class_), key </span><del>- objectstore.global_attributes.register_attribute(class_, key, uselist = self.uselist, deleteremoved = self.private, callable_=lambda i: self.setup_loader(i), extension=self.attributeext, cascade=self.cascade) </del><ins>+ objectstore.global_attributes.register_attribute(class_, key, uselist = self.uselist, callable_=lambda i: self.setup_loader(i), extension=self.attributeext, cascade=self.cascade) </ins><span class="cx"> </span><span class="cx"> def setup_loader(self, instance): </span><span class="cx"> if not self.parent.is_assigned(instance): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingunitofworkpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py (1311 => 1312)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py 2006-04-21 21:22:18 UTC (rev 1311) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py 2006-04-21 21:26:54 UTC (rev 1312) </span><span class="lines">@@ -39,20 +39,13 @@ </span><span class="cx"> class UOWListElement(attributes.ListAttribute): </span><span class="cx"> """overrides ListElement to provide unit-of-work "dirty" hooks when list attributes are modified, </span><span class="cx"> plus specialzed append() method.""" </span><del>- def __init__(self, obj, key, data=None, deleteremoved=False, cascade=None, **kwargs): </del><ins>+ def __init__(self, obj, key, data=None, cascade=None, **kwargs): </ins><span class="cx"> attributes.ListAttribute.__init__(self, obj, key, data=data, **kwargs) </span><del>- self.deleteremoved = deleteremoved </del><span class="cx"> self.cascade = cascade </span><span class="cx"> def value_changed(self, obj, key, item, listval, isdelete): </span><span class="cx"> sess = get_session(obj, raiseerror=False) </span><span class="cx"> if sess is None: </span><span class="cx"> return </span><del>- # add the child item to this session. if its already got an identity then its - # expected to be there already. - #sess.add(item) - #if not isdelete and sess.deleted.contains(item): - #raise InvalidRequestError("re-inserting a deleted value into a list") - # del sess.deleted[item] </del><span class="cx"> sess.modified_lists.append(self) </span><span class="cx"> if self.cascade is not None: </span><span class="cx"> if isdelete: </span><span class="lines">@@ -61,8 +54,6 @@ </span><span class="cx"> else: </span><span class="cx"> if "save-update" in self.cascade: </span><span class="cx"> sess.save_or_update(item) </span><del>- #if self.deleteremoved and isdelete: - # sess._register_deleted(item) </del><span class="cx"> def append(self, item, _mapper_nohistory = False): </span><span class="cx"> if _mapper_nohistory: </span><span class="cx"> self.append_nohistory(item) </span><span class="lines">@@ -70,6 +61,9 @@ </span><span class="cx"> attributes.ListAttribute.append(self, item) </span><span class="cx"> </span><span class="cx"> class UOWScalarElement(attributes.ScalarAttribute): </span><ins>+ def __init__(self, obj, key, cascade=None, **kwargs): + attributes.ScalarAttribute.__init__(self, obj, key, **kwargs) + self.cascade=cascade </ins><span class="cx"> def value_changed(self, oldvalue, newvalue): </span><span class="cx"> obj = self.obj </span><span class="cx"> sess = get_session(obj, raiseerror=False) </span><span class="lines">@@ -78,6 +72,13 @@ </span><span class="cx"> sess._register_dirty(obj) </span><span class="cx"> else: </span><span class="cx"> sess._register_new(obj) </span><ins>+ if self.cascade is not None: + if isdelete: + if "delete-orphan" in self.cascade: + sess.delete(item) + else: + if "save-update" in self.cascade: + sess.save_or_update(item) </ins><span class="cx"> </span><span class="cx"> class UOWAttributeManager(attributes.AttributeManager): </span><span class="cx"> """overrides AttributeManager to provide unit-of-work "dirty" hooks when scalar attribues are modified, plus factory methods for UOWProperrty/UOWListElement.""" </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-21 21:22:31
|
<!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>[1311] sqlalchemy/branches/schema/lib/sqlalchemy: doing some cascade stuff</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1311</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-21 16:22:18 -0500 (Fri, 21 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>doing some cascade stuff</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemaexamplesadjacencytreebyroot_treepy">sqlalchemy/branches/schema/examples/adjacencytree/byroot_tree.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemy__init__py">sqlalchemy/branches/schema/lib/sqlalchemy/__init__.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyattributespy">sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyenginedefaultpy">sqlalchemy/branches/schema/lib/sqlalchemy/engine/default.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingobjectstorepy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingpropertiespy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymappingunitofworkpy">sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemaexamplesadjacencytreebyroot_treepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/examples/adjacencytree/byroot_tree.py (1310 => 1311)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/examples/adjacencytree/byroot_tree.py 2006-04-21 16:38:29 UTC (rev 1310) +++ sqlalchemy/branches/schema/examples/adjacencytree/byroot_tree.py 2006-04-21 21:22:18 UTC (rev 1311) </span><span class="lines">@@ -5,16 +5,15 @@ </span><span class="cx"> """a more advanced example of basic_tree.py. illustrates MapperExtension objects which </span><span class="cx"> add application-specific functionality to a Mapper object.""" </span><span class="cx"> </span><del>-engine = create_engine('sqlite://', echo = True) -#engine = sqlalchemy.engine.create_engine('mysql', {'db':'test', 'host':'127.0.0.1', 'user':'scott'}, echo=True) -#engine = sqlalchemy.engine.create_engine('postgres', {'database':'test', 'host':'127.0.0.1', 'user':'scott', 'password':'tiger'}, echo=True) -#engine = sqlalchemy.engine.create_engine('oracle', {'dsn':os.environ['DSN'], 'user':os.environ['USER'], 'password':os.environ['PASSWORD']}, echo=True) </del><ins>+engine = create_engine('sqlite:///:memory:', echo = True) </ins><span class="cx"> </span><ins>+metadata = BoundMetaData(engine) + </ins><span class="cx"> """create the treenodes table. This is ia basic adjacency list model table. </span><span class="cx"> One additional column, "root_node_id", references a "root node" row and is used </span><span class="cx"> in the 'byroot_tree' example.""" </span><span class="cx"> </span><del>-trees = Table('treenodes', engine, </del><ins>+trees = Table('treenodes', metadata, </ins><span class="cx"> Column('node_id', Integer, Sequence('treenode_id_seq',optional=False), primary_key=True), </span><span class="cx"> Column('parent_node_id', Integer, ForeignKey('treenodes.node_id'), nullable=True), </span><span class="cx"> Column('root_node_id', Integer, ForeignKey('treenodes.node_id'), nullable=True), </span><span class="lines">@@ -23,7 +22,7 @@ </span><span class="cx"> ) </span><span class="cx"> </span><span class="cx"> treedata = Table( </span><del>- "treedata", engine, </del><ins>+ "treedata", metadata, </ins><span class="cx"> Column('data_id', Integer, primary_key=True), </span><span class="cx"> Column('value', String(100), nullable=False) </span><span class="cx"> ) </span><span class="lines">@@ -94,13 +93,13 @@ </span><span class="cx"> """creates an instance of a TreeNode. since the TreeNode constructor requires </span><span class="cx"> the 'name' argument, this method pulls the data from the database row directly.""" </span><span class="cx"> return TreeNode(row[mapper.c.name], _mapper_nohistory=True) </span><del>- def after_insert(self, mapper, instance): </del><ins>+ def after_insert(self, mapper, connection, instance): </ins><span class="cx"> """runs after the insert of a new TreeNode row. The primary key of the row is not determined </span><span class="cx"> until the insert is complete, since most DB's use autoincrementing columns. If this node is </span><span class="cx"> the root node, we will take the new primary key and update it as the value of the node's </span><span class="cx"> "root ID" as well, since its root node is itself.""" </span><span class="cx"> if instance.root is instance: </span><del>- mapper.primarytable.update(TreeNode.c.id==instance.id, values=dict(root_node_id=instance.id)).execute() </del><ins>+ connection.execute(mapper.primarytable.update(TreeNode.c.id==instance.id, values=dict(root_node_id=instance.id))) </ins><span class="cx"> instance.root_id = instance.id </span><span class="cx"> def append_result(self, mapper, row, imap, result, instance, isnew, populate_existing=False): </span><span class="cx"> """runs as results from a SELECT statement are processed, and newly created or already-existing </span><span class="lines">@@ -132,18 +131,18 @@ </span><span class="cx"> trees.create() </span><span class="cx"> </span><span class="cx"> </span><del>-assign_mapper(TreeNode, trees, properties=dict( </del><ins>+mapper(TreeNode, trees, properties=dict( </ins><span class="cx"> id=trees.c.node_id, </span><span class="cx"> name=trees.c.node_name, </span><span class="cx"> parent_id=trees.c.parent_node_id, </span><span class="cx"> root_id=trees.c.root_node_id, </span><span class="cx"> root=relation(TreeNode, primaryjoin=trees.c.root_node_id==trees.c.node_id, foreignkey=trees.c.node_id, lazy=None, uselist=False), </span><del>- children=relation(TreeNode, primaryjoin=trees.c.parent_node_id==trees.c.node_id, lazy=None, uselist=True, private=True), - data=relation(mapper(TreeData, treedata, properties=dict(id=treedata.c.data_id)), private=True, lazy=False) </del><ins>+ children=relation(TreeNode, primaryjoin=trees.c.parent_node_id==trees.c.node_id, lazy=None, uselist=True, cascade="delete,save-update"), + data=relation(mapper(TreeData, treedata, properties=dict(id=treedata.c.data_id)), cascade="delete,save-update", lazy=False) </ins><span class="cx"> </span><span class="cx"> ), extension = TreeLoader()) </span><del>-TreeNode.mapper </del><span class="cx"> </span><ins>+session = create_session() </ins><span class="cx"> </span><span class="cx"> node2 = TreeNode('node2') </span><span class="cx"> node2.append('subnode1') </span><span class="lines">@@ -162,10 +161,11 @@ </span><span class="cx"> print node.print_nodes() </span><span class="cx"> </span><span class="cx"> print "\n\n\n----------------------------" </span><del>-print "Committing:" </del><ins>+print "flushing:" </ins><span class="cx"> print "----------------------------" </span><span class="cx"> </span><del>-objectstore.commit() </del><ins>+session.save(node) +session.flush() </ins><span class="cx"> #sys.exit() </span><span class="cx"> print "\n\n\n----------------------------" </span><span class="cx"> print "Tree After Save:" </span><span class="lines">@@ -190,7 +190,7 @@ </span><span class="cx"> print "\n\n\n----------------------------" </span><span class="cx"> print "Committing:" </span><span class="cx"> print "----------------------------" </span><del>-objectstore.commit() </del><ins>+session.flush() </ins><span class="cx"> </span><span class="cx"> print "\n\n\n----------------------------" </span><span class="cx"> print "Tree After Save:" </span><span class="lines">@@ -205,8 +205,8 @@ </span><span class="cx"> print "tree new where root_id=%d:" % nodeid </span><span class="cx"> print "----------------------------" </span><span class="cx"> </span><del>-objectstore.clear() -t = TreeNode.mapper.select(TreeNode.c.root_id==nodeid, order_by=[TreeNode.c.id])[0] </del><ins>+session.clear() +t = session.query(TreeNode).select(TreeNode.c.root_id==nodeid, order_by=[TreeNode.c.id])[0] </ins><span class="cx"> </span><span class="cx"> print "\n\n\n----------------------------" </span><span class="cx"> print "Full Tree:" </span><span class="lines">@@ -217,8 +217,8 @@ </span><span class="cx"> print "Marking root node as deleted" </span><span class="cx"> print "and committing:" </span><span class="cx"> print "----------------------------" </span><del>-objectstore.delete(t) -objectstore.commit() </del><ins>+session.delete(t) +session.flush() </ins><span class="cx"> </span><span class="cx"> </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemy__init__py"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/__init__.py (1310 => 1311)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/__init__.py 2006-04-21 16:38:29 UTC (rev 1310) +++ sqlalchemy/branches/schema/lib/sqlalchemy/__init__.py 2006-04-21 21:22:18 UTC (rev 1311) </span><span class="lines">@@ -17,8 +17,8 @@ </span><span class="cx"> from sqlalchemy.mapping.objectstore import Session, get_session </span><span class="cx"> </span><span class="cx"> create_engine = sqlalchemy.engine.create_engine </span><ins>+create_session = objectstore.Session </ins><span class="cx"> </span><del>- </del><span class="cx"> def global_connect(*args, **kwargs): </span><span class="cx"> sqlalchemy.schema.default_metadata.connect(*args, **kwargs) </span><span class="cx"> </span><span class="cx">\ No newline at end of file </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyattributespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py (1310 => 1311)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py 2006-04-21 16:38:29 UTC (rev 1310) +++ sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py 2006-04-21 21:22:18 UTC (rev 1311) </span><span class="lines">@@ -119,6 +119,7 @@ </span><span class="cx"> self.obj.__dict__[self.key] = value </span><span class="cx"> if self.extension is not None: </span><span class="cx"> self.extension.set(self.obj, value, orig) </span><ins>+ self.value_changed(orig, value) </ins><span class="cx"> def delattr(self, **kwargs): </span><span class="cx"> orig = self.obj.__dict__.get(self.key, None) </span><span class="cx"> if self.orig is ScalarAttribute.NONE: </span><span class="lines">@@ -126,6 +127,7 @@ </span><span class="cx"> self.obj.__dict__[self.key] = None </span><span class="cx"> if self.extension is not None: </span><span class="cx"> self.extension.set(self.obj, None, orig) </span><ins>+ self.value_changed(orig, None) </ins><span class="cx"> def append(self, obj): </span><span class="cx"> self.setattr(obj) </span><span class="cx"> def remove(self, obj): </span><span class="lines">@@ -136,9 +138,11 @@ </span><span class="cx"> self.orig = ScalarAttribute.NONE </span><span class="cx"> def commit(self): </span><span class="cx"> self.orig = ScalarAttribute.NONE </span><ins>+ def value_changed(self, oldvalue, newvalue): + pass </ins><span class="cx"> def added_items(self): </span><span class="cx"> if self.orig is not ScalarAttribute.NONE: </span><del>- return [self.obj.__dict__[self.key]] </del><ins>+ return [self.obj.__dict__.get(self.key)] </ins><span class="cx"> else: </span><span class="cx"> return [] </span><span class="cx"> def deleted_items(self): </span><span class="lines">@@ -148,7 +152,7 @@ </span><span class="cx"> return [] </span><span class="cx"> def unchanged_items(self): </span><span class="cx"> if self.orig is ScalarAttribute.NONE: </span><del>- return [self.obj.__dict__[self.key]] </del><ins>+ return [self.obj.__dict__.get(self.key)] </ins><span class="cx"> else: </span><span class="cx"> return [] </span><span class="cx"> </span><span class="lines">@@ -177,7 +181,7 @@ </span><span class="cx"> obj.__dict__[key] = [] </span><span class="cx"> </span><span class="cx"> util.HistoryArraySet.__init__(self, list_, readonly=kwargs.get('readonly', False)) </span><del>- def list_value_changed(self, obj, key, item, listval, isdelete): </del><ins>+ def value_changed(self, obj, key, item, listval, isdelete): </ins><span class="cx"> pass </span><span class="cx"> def setattr(self, value, **kwargs): </span><span class="cx"> self.obj.__dict__[self.key] = value </span><span class="lines">@@ -187,21 +191,18 @@ </span><span class="cx"> def _setrecord(self, item): </span><span class="cx"> res = util.HistoryArraySet._setrecord(self, item) </span><span class="cx"> if res: </span><del>- self.list_value_changed(self.obj, self.key, item, self, False) </del><ins>+ self.value_changed(self.obj, self.key, item, self, False) </ins><span class="cx"> if self.extension is not None: </span><span class="cx"> self.extension.append(self.obj, item) </span><span class="cx"> return res </span><span class="cx"> def _delrecord(self, item): </span><span class="cx"> res = util.HistoryArraySet._delrecord(self, item) </span><span class="cx"> if res: </span><del>- self.list_value_changed(self.obj, self.key, item, self, True) </del><ins>+ self.value_changed(self.obj, self.key, item, self, True) </ins><span class="cx"> if self.extension is not None: </span><span class="cx"> self.extension.delete(self.obj, item) </span><span class="cx"> return res </span><span class="cx"> </span><del>-# deprecated -class ListElement(ListAttribute):pass - </del><span class="cx"> class TriggeredAttribute(ManagedAttribute): </span><span class="cx"> """Used by AttributeManager to allow the attaching of a callable item, representing the future value </span><span class="cx"> of a particular attribute on a particular object instance, as the current attribute on an object. </span><span class="lines">@@ -221,7 +222,7 @@ </span><span class="cx"> </span><span class="cx"> def plain_init(self, attrhist): </span><span class="cx"> if not self.uselist: </span><del>- p = ScalarAttribute(self.obj, self.key, **self.kwargs) </del><ins>+ p = self.manager.create_scalar(self.obj, self.key, **self.kwargs) </ins><span class="cx"> self.obj.__dict__[self.key] = None </span><span class="cx"> else: </span><span class="cx"> p = self.manager.create_list(self.obj, self.key, None, **self.kwargs) </span><span class="lines">@@ -247,7 +248,7 @@ </span><span class="cx"> raise AssertionError("AttributeError caught in callable prop:" + str(e.args)) </span><span class="cx"> self.obj.__dict__[self.key] = value </span><span class="cx"> </span><del>- p = ScalarAttribute(self.obj, self.key, **self.kwargs) </del><ins>+ p = self.manager.create_scalar(self.obj, self.key, **self.kwargs) </ins><span class="cx"> else: </span><span class="cx"> if not self.obj.__dict__.has_key(self.key) or len(self.obj.__dict__[self.key]) == 0: </span><span class="cx"> if passive: </span><span class="lines">@@ -320,7 +321,8 @@ </span><span class="cx"> """creates a scalar property object, defaulting to SmartProperty, which </span><span class="cx"> will communicate change events back to this AttributeManager.""" </span><span class="cx"> return SmartProperty(self, key, uselist, callable_, **kwargs) </span><del>- </del><ins>+ def create_scalar(self, obj, key, **kwargs): + return ScalarAttribute(obj, key, **kwargs) </ins><span class="cx"> def create_list(self, obj, key, list_, **kwargs): </span><span class="cx"> """creates a history-aware list property, defaulting to a ListAttribute which </span><span class="cx"> is a subclass of HistoryArrayList.""" </span><span class="lines">@@ -473,7 +475,7 @@ </span><span class="cx"> if callable_ is not None: </span><span class="cx"> prop = self.create_callable(obj, key, callable_, uselist=uselist, **kwargs) </span><span class="cx"> elif not uselist: </span><del>- prop = ScalarAttribute(obj, key, **kwargs) </del><ins>+ prop = self.create_scalar(obj, key, **kwargs) </ins><span class="cx"> else: </span><span class="cx"> prop = self.create_list(obj, key, None, **kwargs) </span><span class="cx"> if attrdict is None: </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyenginedefaultpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/engine/default.py (1310 => 1311)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/default.py 2006-04-21 16:38:29 UTC (rev 1310) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/default.py 2006-04-21 21:22:18 UTC (rev 1311) </span><span class="lines">@@ -24,7 +24,6 @@ </span><span class="cx"> dbapi = dialect.dbapi() </span><span class="cx"> if dbapi is None: </span><span class="cx"> raise exceptions.InvalidRequestException("Cant get DBAPI module for dialect '%s'" % dialect) </span><del>- </del><span class="cx"> self._pool = poolclass(lambda: dbapi.connect(*cargs, **cparams), **kwargs) </span><span class="cx"> else: </span><span class="cx"> if isinstance(pool, sqlalchemy.pool.DBProxy): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py (1310 => 1311)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py 2006-04-21 16:38:29 UTC (rev 1310) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/mapper.py 2006-04-21 21:22:18 UTC (rev 1311) </span><span class="lines">@@ -689,10 +689,16 @@ </span><span class="cx"> prop.register_dependencies(uowcommit, *args, **kwargs) </span><span class="cx"> if self.inherits is not None: </span><span class="cx"> uowcommit.register_dependency(self.inherits, self) </span><del>- - def register_deleted(self, obj, uow): </del><ins>+ + def cascade_iterator(self, type, object): + yield object </ins><span class="cx"> for prop in self.props.values(): </span><del>- prop.register_deleted(obj, uow) </del><ins>+ for c in prop.cascade_iterator(type, object): + yield c + +# def register_deleted(self, obj, uow): +# for prop in self.props.values(): +# prop.register_deleted(obj, uow) </ins><span class="cx"> </span><span class="cx"> </span><span class="cx"> def _identity_key(self, row): </span><span class="lines">@@ -781,6 +787,8 @@ </span><span class="cx"> """called when the mapper receives a row. instance is the parent instance </span><span class="cx"> corresponding to the row. """ </span><span class="cx"> raise NotImplementedError() </span><ins>+ def cascade_iterator(self, type, object): + return [] </ins><span class="cx"> def copy(self): </span><span class="cx"> raise NotImplementedError() </span><span class="cx"> def get_criterion(self, key, value): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py (1310 => 1311)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py 2006-04-21 16:38:29 UTC (rev 1310) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/objectstore.py 2006-04-21 21:22:18 UTC (rev 1311) </span><span class="lines">@@ -222,23 +222,71 @@ </span><span class="cx"> for o in obj: </span><span class="cx"> self.uow.expunge(o) </span><span class="cx"> </span><del>- def add(self, *obj, **kwargs): </del><ins>+ def save(self, *obj, **kwargs): </ins><span class="cx"> """adds unsaved objects to this Session. </span><span class="cx"> </span><span class="cx"> The 'entity_name' keyword argument can also be given which will be assigned </span><span class="cx"> to the instances if given. </span><span class="cx"> """ </span><span class="cx"> for o in obj: </span><del>- if hasattr(o, '_instance_key'): - if not self.uow.has_key(o._instance_key): - raise InvalidRequestError("Instance '%s' is not bound to this Session; use session.import(instance)" % repr(o)) - else: - entity_name = kwargs.get('entity_name', None) - if entity_name is not None: - m = class_mapper(o.__class__, entity_name=entity_name) - m._assign_entity_name(o) - self._register_new(o) - </del><ins>+ for c in object_mapper(o, **kwargs).cascade_iterator('save-update', o): + if c is o: + self._save_impl(c, **kwargs) + else: + self.save_or_update(c, **kwargs) + + def update(self, *obj, **kwargs): + for o in obj: + for c in object_mapper(o, **kwargs).cascade_iterator('save-update', o): + if c is o: + self._update_impl(c, **kwargs) + else: + self.save_or_update(c, **kwargs) + + def save_or_update(self, *obj, **kwargs): + for o in obj: + for c in object_mapper(o, *kwargs).cascade_iterator('save-update', o): + key = self._instance_key(c, raiseerror=False, **kwargs) + if key is None: + self._save_impl(c, **kwargs) + else: + self._update_impl(c, **kwargs) + + def _save_impl(self, object, **kwargs): + if hasattr(object, '_instance_key'): + if not self.uow.has_key(object._instance_key): + raise InvalidRequestError("Instance '%s' attached to a different Session" % repr(object)) + else: + entity_name = kwargs.get('entity_name', None) + if entity_name is not None: + m = class_mapper(object.__class__, entity_name=entity_name) + m._assign_entity_name(object) + self._register_new(object) + + def _update_impl(self, object, **kwargs): + if self._is_bound(object): + return + if not hasattr(object, '_instance_key'): + raise InvalidRequestError("Instance '%s' is not persisted" % repr(object)) + self._register_dirty(object) + + def _instance_key(self, object, entity_name=None, raiseerror=True): + key = getattr(object, '_instance_key', None) + if key is None: + mapper = object_mapper(object, raiseerror=False) + if mapper is None: + mapper = class_mapper(object, entity_name=entity_name) + ident = mapper.identity(object) + for k in ident: + if k is None: + if raiseerror: + raise InvalidRequestError("Instance '%s' does not have a full set of identity values, and does not represent a saved entity in the database. Use the add() method to add unsaved instances to this Session." % str(object)) + else: + return None + key = mapper.identity_key(*ident) + return key + + </ins><span class="cx"> def _register_new(self, obj): </span><span class="cx"> self._bind_to(obj) </span><span class="cx"> self.uow.register_new(obj) </span><span class="lines">@@ -285,11 +333,13 @@ </span><span class="cx"> </span><span class="cx"> the keyword argument 'entity_name' can also be provided which will be used by the import.""" </span><span class="cx"> for o in obj: </span><del>- if not self._is_bound(o): - o = self.import_(o, **kwargs) - self.uow.register_deleted(o) - - def import_(self, instance, entity_name=None): </del><ins>+ for c in object_mapper(o, **kwargs).cascade_iterator('delete', o): + if not self._is_bound(c): + c = self.import_(c, **kwargs) + self.uow.register_deleted(c) + + + def merge(self, instance, entity_name=None): </ins><span class="cx"> """given an instance that represents a saved item, adds it to this session. </span><span class="cx"> the return value is either the given instance, or if an instance corresponding to the </span><span class="cx"> identity of the given instance already exists within this session, then that instance is returned; </span><span class="lines">@@ -307,16 +357,7 @@ </span><span class="cx"> """ </span><span class="cx"> if instance is None: </span><span class="cx"> return None </span><del>- key = getattr(instance, '_instance_key', None) - mapper = object_mapper(instance, raiseerror=False) - if mapper is None: - mapper = class_mapper(instance, entity_name=entity_name) - if key is None: - ident = mapper.identity(instance) - for k in ident: - if k is None: - raise InvalidRequestError("Instance '%s' does not have a full set of identity values, and does not represent a saved entity in the database. Use the add() method to add unsaved instances to this Session." % str(instance)) - key = mapper.identity_key(*ident) </del><ins>+ key = self._instance_key(instance, entity_name=entity_name, raiseerror=True) </ins><span class="cx"> u = self.uow </span><span class="cx"> if u.identity_map.has_key(key): </span><span class="cx"> return u.identity_map[key] </span><span class="lines">@@ -328,7 +369,7 @@ </span><span class="cx"> </span><span class="cx"> def import_instance(self, *args, **kwargs): </span><span class="cx"> """deprecated; a synynom for import()""" </span><del>- return self.import_(*args, **kwargs) </del><ins>+ return self.merge(*args, **kwargs) </ins><span class="cx"> </span><span class="cx"> def get_id_key(ident, class_, entity_name=None): </span><span class="cx"> return Session.get_id_key(ident, class_, entity_name) </span><span class="lines">@@ -339,8 +380,8 @@ </span><span class="cx"> def mapper(*args, **params): </span><span class="cx"> return sqlalchemy.mapping.mapper(*args, **params) </span><span class="cx"> </span><del>-def object_mapper(obj): - return sqlalchemy.mapping.object_mapper(obj) </del><ins>+def object_mapper(obj, **kwargs): + return sqlalchemy.mapping.object_mapper(obj, **kwargs) </ins><span class="cx"> </span><span class="cx"> def class_mapper(class_, **kwargs): </span><span class="cx"> return sqlalchemy.mapping.class_mapper(class_, **kwargs) </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py (1310 => 1311)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py 2006-04-21 16:38:29 UTC (rev 1310) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/properties.py 2006-04-21 21:22:18 UTC (rev 1311) </span><span class="lines">@@ -18,6 +18,7 @@ </span><span class="cx"> import mapper </span><span class="cx"> import objectstore </span><span class="cx"> from sqlalchemy.exceptions import * </span><ins>+import sets </ins><span class="cx"> </span><span class="cx"> class ColumnProperty(MapperProperty): </span><span class="cx"> """describes an object attribute that corresponds to a table column.""" </span><span class="lines">@@ -93,7 +94,7 @@ </span><span class="cx"> if prop is self: </span><span class="cx"> continue </span><span class="cx"> instance.__dict__[prop.key] = row[prop.columns[0]] </span><del>- objectstore.global_attributes.create_history(instance, prop.key, uselist=False) </del><ins>+ objectstore.global_attributes.create_history(instance, prop.key, uselist=False, cascade=self.cascade) </ins><span class="cx"> return row[self.columns[0]] </span><span class="cx"> else: </span><span class="cx"> return connection.scalar(sql.select([self.columns[0]], clause, use_labels=True),None) </span><span class="lines">@@ -105,7 +106,7 @@ </span><span class="cx"> def execute(self, session, instance, row, identitykey, imap, isnew): </span><span class="cx"> if isnew: </span><span class="cx"> if not self.is_primary(): </span><del>- objectstore.global_attributes.create_history(instance, self.key, False, callable_=self.setup_loader(instance)) </del><ins>+ objectstore.global_attributes.create_history(instance, self.key, False, callable_=self.setup_loader(instance), cascade=self.cascade) </ins><span class="cx"> else: </span><span class="cx"> objectstore.global_attributes.reset_history(instance, self.key) </span><span class="cx"> </span><span class="lines">@@ -118,7 +119,7 @@ </span><span class="cx"> </span><span class="cx"> """describes an object property that holds a single item or list of items that correspond </span><span class="cx"> to a related database table.""" </span><del>- def __init__(self, argument, secondary, primaryjoin, secondaryjoin, foreignkey=None, uselist=None, private=False, association=None, use_alias=None, selectalias=None, order_by=False, attributeext=None, backref=None, is_backref=False, post_update=False): </del><ins>+ def __init__(self, argument, secondary, primaryjoin, secondaryjoin, foreignkey=None, uselist=None, private=False, association=None, use_alias=None, selectalias=None, order_by=False, attributeext=None, backref=None, is_backref=False, post_update=False, cascade=None): </ins><span class="cx"> self.uselist = uselist </span><span class="cx"> self.argument = argument </span><span class="cx"> self.secondary = secondary </span><span class="lines">@@ -136,8 +137,17 @@ </span><span class="cx"> self.foreigntable = foreignkey.table </span><span class="cx"> else: </span><span class="cx"> self.foreigntable = None </span><del>- - self.private = private </del><ins>+ + # hibernate cascades: + # create, merge, save-update, delete, lock, refresh, evict, replicate. + if cascade is not None: + self.cascade = sets.Set([c.strip() for c in cascade.split(',')]) + else: + if private: + self.cascade = sets.Set(["delete-orphan", "delete"]) + else: + self.cascade = sets.Set() + </ins><span class="cx"> self.association = association </span><span class="cx"> if selectalias is not None: </span><span class="cx"> print "'selectalias' argument to relation() is deprecated. eager loads automatically alias-ize tables now." </span><span class="lines">@@ -151,6 +161,23 @@ </span><span class="cx"> self.backref = backref </span><span class="cx"> self.is_backref = is_backref </span><span class="cx"> </span><ins>+ private = property(lambda s:"delete-orphan" in s.cascade) + + def cascade_iterator(self, type, object): + + if not type in self.cascade: + return + + if self.uselist: + childlist = objectstore.global_attributes.get_history(object, self.key, passive = False) + else: + childlist = objectstore.global_attributes.get_history(object, self.key) + for c in childlist.added_items() + childlist.deleted_items() + childlist.unchanged_items(): + if c is not None: + yield c + for c2 in self.mapper.cascade_iterator(type, c): + yield c2 + </ins><span class="cx"> def copy(self): </span><span class="cx"> x = self.__class__.__new__(self.__class__) </span><span class="cx"> x.__dict__.update(self.__dict__) </span><span class="lines">@@ -226,7 +253,7 @@ </span><span class="cx"> </span><span class="cx"> def _set_class_attribute(self, class_, key): </span><span class="cx"> """sets attribute behavior on our target class.""" </span><del>- objectstore.global_attributes.register_attribute(class_, key, uselist = self.uselist, deleteremoved = self.private, extension=self.attributeext) </del><ins>+ objectstore.global_attributes.register_attribute(class_, key, uselist = self.uselist, deleteremoved = self.private, extension=self.attributeext, cascade=self.cascade) </ins><span class="cx"> </span><span class="cx"> def _get_direction(self): </span><span class="cx"> """determines our 'direction', i.e. do we represent one to many, many to many, etc.""" </span><span class="lines">@@ -302,18 +329,7 @@ </span><span class="cx"> return c.copy_container() </span><span class="cx"> return None </span><span class="cx"> </span><del>- def register_deleted(self, obj, uow): - if not self.private: - return </del><span class="cx"> </span><del>- if self.uselist: - childlist = uow.attributes.get_history(obj, self.key, passive = False) - else: - childlist = uow.attributes.get_history(obj, self.key) - for child in childlist.deleted_items() + childlist.unchanged_items(): - if child is not None: - uow.register_deleted(child) - </del><span class="cx"> class MapperStub(object): </span><span class="cx"> """poses as a Mapper representing the association table in a many-to-many </span><span class="cx"> join, when performing a commit(). </span><span class="lines">@@ -546,7 +562,7 @@ </span><span class="cx"> if self.is_primary(): </span><span class="cx"> return </span><span class="cx"> #print "PLAIN PROPLOADER EXEC NON-PRIAMRY", repr(id(self)), repr(self.mapper.class_), self.key </span><del>- objectstore.global_attributes.create_history(instance, self.key, self.uselist) </del><ins>+ objectstore.global_attributes.create_history(instance, self.key, self.uselist, cascade=self.cascade) </ins><span class="cx"> </span><span class="cx"> def _compile_synchronizers(self): </span><span class="cx"> """assembles a list of 'synchronization rules', which are instructions on how to populate </span><span class="lines">@@ -596,7 +612,7 @@ </span><span class="cx"> def _set_class_attribute(self, class_, key): </span><span class="cx"> # establish a class-level lazy loader on our class </span><span class="cx"> #print "SETCLASSATTR LAZY", repr(class_), key </span><del>- objectstore.global_attributes.register_attribute(class_, key, uselist = self.uselist, deleteremoved = self.private, callable_=lambda i: self.setup_loader(i), extension=self.attributeext) </del><ins>+ objectstore.global_attributes.register_attribute(class_, key, uselist = self.uselist, deleteremoved = self.private, callable_=lambda i: self.setup_loader(i), extension=self.attributeext, cascade=self.cascade) </ins><span class="cx"> </span><span class="cx"> def setup_loader(self, instance): </span><span class="cx"> if not self.parent.is_assigned(instance): </span><span class="lines">@@ -644,7 +660,7 @@ </span><span class="cx"> #print "EXEC NON-PRIAMRY", repr(self.mapper.class_), self.key </span><span class="cx"> # we are not the primary manager for this attribute on this class - set up a per-instance lazyloader, </span><span class="cx"> # which will override the class-level behavior </span><del>- objectstore.global_attributes.create_history(instance, self.key, self.uselist, callable_=self.setup_loader(instance)) </del><ins>+ objectstore.global_attributes.create_history(instance, self.key, self.uselist, callable_=self.setup_loader(instance), cascade=self.cascade) </ins><span class="cx"> else: </span><span class="cx"> #print "EXEC PRIMARY", repr(self.mapper.class_), self.key </span><span class="cx"> # we are the primary manager for this attribute on this class - reset its per-instance attribute state, </span><span class="lines">@@ -795,7 +811,7 @@ </span><span class="cx"> if isnew: </span><span class="cx"> # new row loaded from the database. initialize a blank container on the instance. </span><span class="cx"> # this will override any per-class lazyloading type of stuff. </span><del>- h = objectstore.global_attributes.create_history(instance, self.key, self.uselist) </del><ins>+ h = objectstore.global_attributes.create_history(instance, self.key, self.uselist, cascade=self.cascade) </ins><span class="cx"> </span><span class="cx"> if not self.uselist: </span><span class="cx"> if isnew: </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymappingunitofworkpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py (1310 => 1311)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py 2006-04-21 16:38:29 UTC (rev 1310) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mapping/unitofwork.py 2006-04-21 21:22:18 UTC (rev 1311) </span><span class="lines">@@ -34,48 +34,62 @@ </span><span class="cx"> super(UOWProperty, self).__init__(*args, **kwargs) </span><span class="cx"> self.class_ = class_ </span><span class="cx"> property = property(lambda s:class_mapper(s.class_).props[s.key], doc="returns the MapperProperty object associated with this property") </span><del>- -class UOWListElement(attributes.ListElement): </del><ins>+ + +class UOWListElement(attributes.ListAttribute): </ins><span class="cx"> """overrides ListElement to provide unit-of-work "dirty" hooks when list attributes are modified, </span><span class="cx"> plus specialzed append() method.""" </span><del>- def __init__(self, obj, key, data=None, deleteremoved=False, **kwargs): - attributes.ListElement.__init__(self, obj, key, data=data, **kwargs) </del><ins>+ def __init__(self, obj, key, data=None, deleteremoved=False, cascade=None, **kwargs): + attributes.ListAttribute.__init__(self, obj, key, data=data, **kwargs) </ins><span class="cx"> self.deleteremoved = deleteremoved </span><del>- def list_value_changed(self, obj, key, item, listval, isdelete): - sess = get_session(obj, raiseerror=True) </del><ins>+ self.cascade = cascade + def value_changed(self, obj, key, item, listval, isdelete): + sess = get_session(obj, raiseerror=False) </ins><span class="cx"> if sess is None: </span><span class="cx"> return </span><span class="cx"> # add the child item to this session. if its already got an identity then its </span><span class="cx"> # expected to be there already. </span><del>- sess.add(item) - if not isdelete and sess.deleted.contains(item): </del><ins>+ #sess.add(item) + #if not isdelete and sess.deleted.contains(item): </ins><span class="cx"> #raise InvalidRequestError("re-inserting a deleted value into a list") </span><del>- del sess.deleted[item] </del><ins>+ # del sess.deleted[item] </ins><span class="cx"> sess.modified_lists.append(self) </span><del>- if self.deleteremoved and isdelete: - sess._register_deleted(item) </del><ins>+ if self.cascade is not None: + if isdelete: + if "delete-orphan" in self.cascade: + sess.delete(item) + else: + if "save-update" in self.cascade: + sess.save_or_update(item) + #if self.deleteremoved and isdelete: + # sess._register_deleted(item) </ins><span class="cx"> def append(self, item, _mapper_nohistory = False): </span><span class="cx"> if _mapper_nohistory: </span><span class="cx"> self.append_nohistory(item) </span><span class="cx"> else: </span><del>- attributes.ListElement.append(self, item) - -class UOWAttributeManager(attributes.AttributeManager): - """overrides AttributeManager to provide unit-of-work "dirty" hooks when scalar attribues are modified, plus factory methods for UOWProperrty/UOWListElement.""" - def __init__(self): - attributes.AttributeManager.__init__(self) - - def value_changed(self, obj, key, value): </del><ins>+ attributes.ListAttribute.append(self, item) + +class UOWScalarElement(attributes.ScalarAttribute): + def value_changed(self, oldvalue, newvalue): + obj = self.obj </ins><span class="cx"> sess = get_session(obj, raiseerror=False) </span><span class="cx"> if sess is not None: </span><span class="cx"> if hasattr(obj, '_instance_key'): </span><span class="cx"> sess._register_dirty(obj) </span><span class="cx"> else: </span><span class="cx"> sess._register_new(obj) </span><del>- </del><ins>+ +class UOWAttributeManager(attributes.AttributeManager): + """overrides AttributeManager to provide unit-of-work "dirty" hooks when scalar attribues are modified, plus factory methods for UOWProperrty/UOWListElement.""" + def __init__(self): + attributes.AttributeManager.__init__(self) + </ins><span class="cx"> def create_prop(self, class_, key, uselist, callable_, **kwargs): </span><span class="cx"> return UOWProperty(class_, self, key, uselist, callable_, **kwargs) </span><span class="cx"> </span><ins>+ def create_scalar(self, obj, key, **kwargs): + return UOWScalarElement(obj, key, **kwargs) + </ins><span class="cx"> def create_list(self, obj, key, list_, **kwargs): </span><span class="cx"> return UOWListElement(obj, key, list_, **kwargs) </span><span class="cx"> </span><span class="lines">@@ -201,10 +215,6 @@ </span><span class="cx"> if not self.deleted.contains(obj): </span><span class="cx"> self._validate_obj(obj) </span><span class="cx"> self.deleted.append(obj) </span><del>- mapper = object_mapper(obj) - # TODO: should the cascading delete dependency thing - # happen wtihin PropertyLoader.process_dependencies ? - mapper.register_deleted(obj, self) </del><span class="cx"> </span><span class="cx"> def unregister_deleted(self, obj): </span><span class="cx"> try: </span><span class="lines">@@ -226,6 +236,7 @@ </span><span class="cx"> if self.deleted.contains(obj): </span><span class="cx"> continue </span><span class="cx"> flush_context.register_object(obj) </span><ins>+ </ins><span class="cx"> for item in self.modified_lists: </span><span class="cx"> obj = item.obj </span><span class="cx"> if objset is not None and not objset.contains(obj): </span><span class="lines">@@ -662,7 +673,13 @@ </span><span class="cx"> continue </span><span class="cx"> if not o in childtask.objects: </span><span class="cx"> # item needs to be saved since its added, or attached to a deleted object </span><del>- childtask.append(o, isdelete=isdelete and dep.processor.private) </del><ins>+ if isdelete: + childtask.append(o, "delete" in processor.cascade) + #if isdelete and "delete" in processor.cascade: + # childstask.append(o, True) + #elif not isdelete and "save-update" in processor.cascade: + # childtask.append(o, False) + #childtask.append(o, isdelete=isdelete and dep.processor.private) </ins><span class="cx"> if cyclicaldep: </span><span class="cx"> # cyclical, so create a placeholder UOWTask that may be built into the </span><span class="cx"> # final task tree </span><span class="lines">@@ -693,11 +710,12 @@ </span><span class="cx"> """takes a dependency-sorted tree of objects and creates a tree of UOWTasks""" </span><span class="cx"> t = objecttotask[node.item] </span><span class="cx"> can_add_to_parent = t.mapper is parenttask.mapper </span><del>- if can_add_to_parent: - parenttask.append(node.item, t.parent.objects[node.item].listonly, isdelete=t.parent.objects[node.item].isdelete, childtask=t) - else: - t.append(node.item, t.parent.objects[node.item].listonly, isdelete=t.parent.objects[node.item].isdelete) - parenttask.append(None, listonly=False, isdelete=t.parent.objects[node.item].isdelete, childtask=t) </del><ins>+ if t.parent.objects.has_key(node.item): + if can_add_to_parent: + parenttask.append(node.item, t.parent.objects[node.item].listonly, isdelete=t.parent.objects[node.item].isdelete, childtask=t) + else: + t.append(node.item, t.parent.objects[node.item].listonly, isdelete=t.parent.objects[node.item].isdelete) + parenttask.append(None, listonly=False, isdelete=t.parent.objects[node.item].isdelete, childtask=t) </ins><span class="cx"> if dependencies.has_key(node.item): </span><span class="cx"> for depprocessor, deptask in dependencies[node.item].iteritems(): </span><span class="cx"> if can_add_to_parent: </span></span></pre> </div> </div> </body> </html> |