Author: ianb
Date: Mon Feb 9 20:02:50 2004
New Revision: 9
Modified:
trunk/SQLObject/sqlobject/ (props changed)
trunk/SQLObject/sqlobject/include/ (props changed)
trunk/SQLObject/sqlobject/main.py
trunk/SQLObject/tests/SQLObjectTest.py
trunk/SQLObject/tests/test.py
Log:
Added lazy updating
Some URI-related fixes
Modified: trunk/SQLObject/sqlobject/main.py
==============================================================================
--- trunk/SQLObject/sqlobject/main.py (original)
+++ trunk/SQLObject/sqlobject/main.py Mon Feb 9 20:02:50 2004
@@ -80,6 +80,16 @@
if not d.has_key('_table'):
d['_table'] = None
+ if d.has_key('_connection'):
+ connection = d['_connection']
+ del d['_connection']
+ assert not d.has_key('connection')
+ elif d.has_key('connection'):
+ connection = d['connection']
+ del d['connection']
+ else:
+ connection = None
+
# We actually create the class.
newClass = type.__new__(cls, className, bases, d)
newClass._SO_finishedClassCreation = False
@@ -96,19 +106,14 @@
######################################################
# Set some attributes to their defaults, if necessary.
# First we get the connection:
- if not newClass._connection:
-
+ if not connection and not getattr(newClass, '_connection', None):
mod = sys.modules[newClass.__module__]
# See if there's a __connection__ global in
# the module, use it if there is.
if hasattr(mod, '__connection__'):
- newClass._connection = mod.__connection__
+ connection = mod.__connection__
- # If the connection is named, we turn the name into
- # a real connection.
- if isinstance(newClass._connection, str):
- newClass._connection = dbconnection.openURI(
- newClass._connection)
+ newClass.setConnection(connection)
# The style object tells how to map between Python
# identifiers and Database identifiers:
@@ -309,6 +314,8 @@
# when necessary: (bad clever? maybe)
_expired = False
+ _lazyUpdate = False
+
def get(cls, id, connection=None, selectResults=None):
assert id is not None, 'None is not a possible id for %s' % cls.__name
@@ -597,6 +604,7 @@
if not selectResults:
raise SQLObjectNotFound, "The object %s by the ID %s does not exist" % (self.__class__.__name__, self.id)
self._SO_selectInit(selectResults)
+ self.dirty = 0
def _SO_loadValue(self, attrName):
try:
@@ -628,6 +636,8 @@
self._SO_writeLock.release()
def sync(self):
+ if self._lazyUpdate and self._SO_createValues:
+ self.syncUpdate()
self._SO_writeLock.acquire()
try:
dbNames = [col.dbName for col in self._SO_columns]
@@ -639,6 +649,20 @@
finally:
self._SO_writeLock.release()
+ def syncUpdate(self):
+ if not self._SO_createValues:
+ return
+ print 'UP:', self._SO_createValues
+ self._SO_writeLock.acquire()
+ try:
+ if self._SO_columnDict:
+ values = [(self._SO_columnDict[v[0]].dbName, v[1]) for v in self._SO_createValues.items()]
+ self._connection._SO_update(self, values)
+ self.dirty = False
+ self._SO_createValues = {}
+ finally:
+ self._SO_writeLock.release()
+
def expire(self):
if self._expired:
return
@@ -663,8 +687,10 @@
# dictionary until later:
if fromPython:
value = fromPython(value, self._SO_validatorState)
- if self._SO_creating:
+ if self._SO_creating or self._lazyUpdate:
+ self.dirty = True
self._SO_createValues[name] = value
+ setattr(self, instanceName(name), value)
return
self._connection._SO_update(self,
@@ -679,12 +705,14 @@
# potentially with one SQL statement if possible.
# _SO_creating is special, see _SO_setValue
- if self._SO_creating:
+ if self._SO_creating or self._lazyUpdate:
for name, value in kw.items():
fromPython = getattr(self, '_SO_fromPython_%s' % name)
if fromPython:
kw[name] = fromPython(value, self._SO_validatorState)
self._SO_createValues.update(kw)
+ self.dirty = True
+ setattr(self, instanceName(name), value)
return
self._SO_writeLock.acquire()
@@ -753,7 +781,7 @@
return
# Pass the connection object along if we were given one.
- # Passing None for the ID tells __new__ we want to create
+ # Passing None for the ID tells __init__ we want to create
# a new object.
if kw.has_key('connection'):
self._connection = kw['connection']
@@ -788,7 +816,7 @@
# If we don't get it, it's an error:
if default is NoDefault:
- raise TypeError, "%s did not get expected keyword argument %s" % (cls.__name__, repr(column.name))
+ raise TypeError, "%s() did not get expected keyword argument %s" % (self.__class__.__name__, column.name)
# Otherwise we put it in as though they did pass
# that keyword:
kw[column.name] = default
@@ -829,7 +857,11 @@
# Get rid of _SO_create*, we aren't creating anymore.
# Doesn't have to be threadsafe because we're still in
# new(), which doesn't need to be threadsafe.
- del self._SO_createValues
+ self.dirty = False
+ if not self._lazyUpdate:
+ del self._SO_createValues
+ else:
+ self._SO_createValues = {}
del self._SO_creating
# Do the insert -- most of the SQL in this case is left
@@ -1001,6 +1033,11 @@
items.append((col.name, getattr(self, col.name)))
return items
+ def setConnection(cls, value):
+ if isinstance(value, (str, unicode)):
+ value = dbconnection.openURI(value)
+ cls._connection = value
+ setConnection = classmethod(setConnection)
def capitalize(name):
return name[0].capitalize() + name[1:]
@@ -1151,6 +1188,7 @@
self.soObject = soObject
self.protocol = 'sql'
+
########################################
## Utility functions (for external consumption)
Modified: trunk/SQLObject/tests/SQLObjectTest.py
==============================================================================
--- trunk/SQLObject/tests/SQLObjectTest.py (original)
+++ trunk/SQLObject/tests/SQLObjectTest.py Mon Feb 9 20:02:50 2004
@@ -1,5 +1,7 @@
import unittest
from sqlobject import *
+from sqlobject.dbconnection import openURI
+import os
True, False = 1==1, 0==1
@@ -11,60 +13,49 @@
SQLObjectTest.supportRestrictedEnum = False
# Technically it does, but now how we're using it:
SQLObjectTest.supportTransactions = False
- return MySQLConnection(host='localhost',
- db='test',
- user='test',
- passwd='',
- debug=0)
+ return 'mysql://test@localhost/test'
def dbmConnection():
SQLObjectTest.supportDynamic = True
SQLObjectTest.supportAuto = False
SQLObjectTest.supportRestrictedEnum = False
SQLObjectTest.supportTransactions = False
- return DBMConnection('data')
+ return 'dbm:///data'
def postgresConnection():
SQLObjectTest.supportDynamic = True
SQLObjectTest.supportAuto = True
SQLObjectTest.supportRestrictedEnum = True
SQLObjectTest.supportTransactions = True
- return PostgresConnection(db='test')
+ return 'postgres://localhost/test'
def pygresConnection():
SQLObjectTest.supportDynamic = True
SQLObjectTest.supportAuto = True
SQLObjectTest.supportRestrictedEnum = True
SQLObjectTest.supportTransactions = True
- return PostgresConnection(db='test', usePygresql=True)
+ return 'pygresql://localhost/test'
def sqliteConnection():
SQLObjectTest.supportDynamic = False
SQLObjectTest.supportAuto = False
SQLObjectTest.supportRestrictedEnum = False
SQLObjectTest.supportTransactions = True
- return SQLiteConnection('data/sqlite.data')
-
+ return 'sqlite:///%s/data/sqlite.data' % os.getcwd()
def sybaseConnection():
SQLObjectTest.supportDynamic = False
SQLObjectTest.supportAuto = False
SQLObjectTest.supportRestrictedEnum = False
SQLObjectTest.supportTransactions = True
- return SybaseConnection(host='localhost',
- db='test',
- user='sa',
- passwd='sybasesa',
- autoCommit=1)
+ return 'sybase://sa:sybasesa@localhost/test'
def firebirdConnection():
SQLObjectTest.supportDynamic = True
SQLObjectTest.supportAuto = False
SQLObjectTest.supportRestrictedEnum = True
SQLObjectTest.supportTransactions = True
- return FirebirdConnection('localhost', '/var/lib/firebird/data/test.gdb',
- user='sysdba', passwd='masterkey')
-
+ return 'firebird://sysdba:masterkey@localhost/var/lib/firebird/data/test.gdb'
_supportedDatabases = {
'mysql': 'MySQLdb',
@@ -96,6 +87,9 @@
databaseName = None
def setUp(self):
+ global __connection__
+ if isinstance(__connection__, str):
+ __connection__ = openURI(__connection__)
if self.debugSQL:
print
print '#' * 70
Modified: trunk/SQLObject/tests/test.py
==============================================================================
--- trunk/SQLObject/tests/test.py (original)
+++ trunk/SQLObject/tests/test.py Mon Feb 9 20:02:50 2004
@@ -900,6 +900,97 @@
self.assertEqual(st1.st2, st2)
########################################
+## Lazy updates
+########################################
+
+class Lazy(SQLObject):
+
+ _lazyUpdate = True
+ name = StringCol()
+ other = StringCol(default='nothing')
+
+class LazyTest(SQLObjectTest):
+
+ classes = [Lazy]
+
+ def setUp(self):
+ SQLObjectTest.setUp(self)
+ self.conn = Lazy._connection
+ self.conn.didUpdate = False
+ oldUpdate = self.conn._SO_update
+ newUpdate = lambda so, values, s=self, c=self.conn, o=oldUpdate: self._alternateUpdate(so, values, c, o)
+ self.conn._SO_update = newUpdate
+
+ def _alternateUpdate(self, so, values, conn, oldUpdate):
+ conn.didUpdate = True
+ return oldUpdate(so, values)
+
+ def test(self):
+ assert not self.conn.didUpdate
+ obj = Lazy(name='tim')
+ # We just did an insert, but not an update:
+ assert not self.conn.didUpdate
+ obj.set(name='joe')
+ assert obj.dirty
+ self.assertEqual(obj.name, 'joe')
+ assert not self.conn.didUpdate
+ obj.syncUpdate()
+ self.assertEqual(obj.name, 'joe')
+ assert self.conn.didUpdate
+ assert not obj.dirty
+ self.assertEqual(obj.name, 'joe')
+ self.conn.didUpdate = False
+
+ obj = Lazy(name='frank')
+ obj.name = 'joe'
+ assert not self.conn.didUpdate
+ assert obj.dirty
+ self.assertEqual(obj.name, 'joe')
+ obj.name = 'joe2'
+ assert not self.conn.didUpdate
+ assert obj.dirty
+ self.assertEqual(obj.name, 'joe2')
+ obj.syncUpdate()
+ self.assertEqual(obj.name, 'joe2')
+ assert not obj.dirty
+ assert self.conn.didUpdate
+ self.conn.didUpdate = False
+
+ obj = Lazy(name='loaded')
+ assert not obj.dirty
+ assert not self.conn.didUpdate
+ self.assertEqual(obj.name, 'loaded')
+ obj.name = 'unloaded'
+ assert obj.dirty
+ self.assertEqual(obj.name, 'unloaded')
+ assert not self.conn.didUpdate
+ obj.sync()
+ assert not obj.dirty
+ self.assertEqual(obj.name, 'unloaded')
+ assert self.conn.didUpdate
+ self.conn.didUpdate = False
+ obj.name = 'whatever'
+ assert obj.dirty
+ self.assertEqual(obj.name, 'whatever')
+ assert not self.conn.didUpdate
+ obj._SO_loadValue('name')
+ assert obj.dirty
+ self.assertEqual(obj.name, 'whatever')
+ assert not self.conn.didUpdate
+ obj._SO_loadValue('other')
+ self.assertEqual(obj.name, 'whatever')
+ assert not self.conn.didUpdate
+ obj.syncUpdate()
+ assert self.conn.didUpdate
+ self.conn.didUpdate = False
+
+ obj = Lazy(name='last')
+ assert not obj.dirty
+ obj.syncUpdate()
+ assert not self.conn.didUpdate
+ assert not obj.dirty
+
+########################################
## Run from command-line:
########################################
|