sqlobject-cvs Mailing List for SQLObject (Page 181)
SQLObject is a Python ORM.
Brought to you by:
ianbicking,
phd
You can subscribe to this list here.
| 2003 |
Jan
|
Feb
|
Mar
(9) |
Apr
(74) |
May
(29) |
Jun
(16) |
Jul
(28) |
Aug
(10) |
Sep
(57) |
Oct
(9) |
Nov
(29) |
Dec
(12) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2004 |
Jan
(7) |
Feb
(14) |
Mar
(6) |
Apr
(3) |
May
(12) |
Jun
(34) |
Jul
(9) |
Aug
(29) |
Sep
(22) |
Oct
(2) |
Nov
(15) |
Dec
(52) |
| 2005 |
Jan
(47) |
Feb
(78) |
Mar
(14) |
Apr
(35) |
May
(33) |
Jun
(16) |
Jul
(26) |
Aug
(63) |
Sep
(40) |
Oct
(96) |
Nov
(96) |
Dec
(123) |
| 2006 |
Jan
(159) |
Feb
(144) |
Mar
(64) |
Apr
(31) |
May
(88) |
Jun
(48) |
Jul
(16) |
Aug
(64) |
Sep
(87) |
Oct
(92) |
Nov
(56) |
Dec
(76) |
| 2007 |
Jan
(94) |
Feb
(103) |
Mar
(126) |
Apr
(123) |
May
(85) |
Jun
(11) |
Jul
(130) |
Aug
(47) |
Sep
(65) |
Oct
(70) |
Nov
(12) |
Dec
(11) |
| 2008 |
Jan
(30) |
Feb
(55) |
Mar
(88) |
Apr
(20) |
May
(50) |
Jun
|
Jul
(38) |
Aug
(1) |
Sep
(9) |
Oct
(5) |
Nov
(6) |
Dec
(39) |
| 2009 |
Jan
(8) |
Feb
(16) |
Mar
(3) |
Apr
(33) |
May
(44) |
Jun
(1) |
Jul
(10) |
Aug
(33) |
Sep
(74) |
Oct
(22) |
Nov
|
Dec
(15) |
| 2010 |
Jan
(28) |
Feb
(22) |
Mar
(46) |
Apr
(29) |
May
(1) |
Jun
(1) |
Jul
(27) |
Aug
(8) |
Sep
(5) |
Oct
(33) |
Nov
(24) |
Dec
(41) |
| 2011 |
Jan
(4) |
Feb
(12) |
Mar
(35) |
Apr
(29) |
May
(19) |
Jun
(16) |
Jul
(32) |
Aug
(25) |
Sep
(5) |
Oct
(11) |
Nov
(21) |
Dec
(12) |
| 2012 |
Jan
(3) |
Feb
(4) |
Mar
(20) |
Apr
(4) |
May
(25) |
Jun
(13) |
Jul
|
Aug
|
Sep
(2) |
Oct
(25) |
Nov
(9) |
Dec
(1) |
| 2013 |
Jan
(6) |
Feb
(8) |
Mar
|
Apr
(10) |
May
(31) |
Jun
(7) |
Jul
(18) |
Aug
(33) |
Sep
(4) |
Oct
(16) |
Nov
|
Dec
(27) |
| 2014 |
Jan
(2) |
Feb
|
Mar
|
Apr
(11) |
May
(39) |
Jun
(8) |
Jul
(11) |
Aug
(4) |
Sep
|
Oct
(27) |
Nov
|
Dec
(71) |
| 2015 |
Jan
(17) |
Feb
(47) |
Mar
(33) |
Apr
|
May
|
Jun
(9) |
Jul
(7) |
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(8) |
| 2016 |
Jan
(4) |
Feb
(4) |
Mar
|
Apr
|
May
(12) |
Jun
(7) |
Jul
(9) |
Aug
(31) |
Sep
(8) |
Oct
(3) |
Nov
(15) |
Dec
(1) |
| 2017 |
Jan
(13) |
Feb
(7) |
Mar
(14) |
Apr
(8) |
May
(10) |
Jun
(4) |
Jul
(2) |
Aug
(1) |
Sep
|
Oct
(8) |
Nov
(4) |
Dec
(5) |
| 2018 |
Jan
(2) |
Feb
(8) |
Mar
|
Apr
(4) |
May
|
Jun
(6) |
Jul
|
Aug
(1) |
Sep
|
Oct
|
Nov
(1) |
Dec
|
| 2019 |
Jan
(1) |
Feb
(16) |
Mar
(1) |
Apr
(3) |
May
(5) |
Jun
(1) |
Jul
|
Aug
|
Sep
(2) |
Oct
|
Nov
(1) |
Dec
(3) |
| 2020 |
Jan
|
Feb
|
Mar
|
Apr
(1) |
May
(1) |
Jun
|
Jul
|
Aug
(1) |
Sep
|
Oct
(2) |
Nov
|
Dec
(2) |
| 2021 |
Jan
|
Feb
(2) |
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
(1) |
Nov
(1) |
Dec
|
| 2022 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
(6) |
Oct
(1) |
Nov
(1) |
Dec
(4) |
| 2023 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
(1) |
Aug
(3) |
Sep
(2) |
Oct
(2) |
Nov
(4) |
Dec
|
| 2024 |
Jan
|
Feb
(2) |
Mar
|
Apr
|
May
|
Jun
|
Jul
(1) |
Aug
|
Sep
(1) |
Oct
|
Nov
|
Dec
(9) |
| 2025 |
Jan
|
Feb
(4) |
Mar
(2) |
Apr
|
May
|
Jun
|
Jul
|
Aug
(1) |
Sep
|
Oct
|
Nov
(2) |
Dec
(2) |
|
From: <sub...@co...> - 2004-02-10 05:13:56
|
Author: ianb Date: Mon Feb 9 20:06:27 2004 New Revision: 10 Modified: trunk/SQLObject/docs/News.txt Log: Added notes about URIs and lazy updates Modified: trunk/SQLObject/docs/News.txt ============================================================================== --- trunk/SQLObject/docs/News.txt (original) +++ trunk/SQLObject/docs/News.txt Mon Feb 9 20:06:27 2004 @@ -13,6 +13,15 @@ Interface Changes ----------------- +* Lazy updates. Add ``_lazyUpdate=True`` to your class, and updates + will only be written when you call ``obj.syncUpdate()`` or + ``obj.sync()`` (``sync`` also refetches the data from the database, + which ``syncUpdate`` does not do). When enabled, instances have a + property ``dirty``, which indicates if they have pending updates. +* Separated database drivers (PostgresConnection, MySQLConnection, + etc.) into separate packages. You can access the driver through + URIs, like ``mysql://user:pass@host/dbname`` -- to set drivers after + class creation you should use `sqlobject.dbconnection.openURI()`. * The ``SQLObject`` package has been renamed to ``sqlobject``. This makes it similar to several other packages, and emphasizes the distinction between the ``sqlobject`` package and the ``SQLObject`` |
|
From: <sub...@co...> - 2004-02-10 05:10:27
|
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:
########################################
|
|
From: <sub...@co...> - 2004-02-08 09:47:21
|
Author: ianb
Date: Sun Feb 8 00:39:59 2004
New Revision: 8
Added:
trunk/SQLObject/sqlobject/dbm/
trunk/SQLObject/sqlobject/dbm/__init__.py
trunk/SQLObject/sqlobject/dbm/dbmconnection.py
trunk/SQLObject/sqlobject/firebird/
trunk/SQLObject/sqlobject/firebird/__init__.py
trunk/SQLObject/sqlobject/firebird/firebirdconnection.py
trunk/SQLObject/sqlobject/mysql/
trunk/SQLObject/sqlobject/mysql/__init__.py
trunk/SQLObject/sqlobject/mysql/mysqlconnection.py
trunk/SQLObject/sqlobject/postgres/
trunk/SQLObject/sqlobject/postgres/__init__.py
trunk/SQLObject/sqlobject/postgres/pgconnection.py
trunk/SQLObject/sqlobject/sqlite/
trunk/SQLObject/sqlobject/sqlite/__init__.py
trunk/SQLObject/sqlobject/sqlite/sqliteconnection.py
trunk/SQLObject/sqlobject/sybase/
trunk/SQLObject/sqlobject/sybase/__init__.py
trunk/SQLObject/sqlobject/sybase/sybaseconnection.py
Modified:
trunk/SQLObject/docs/SQLObject.txt
trunk/SQLObject/examples/config.py
trunk/SQLObject/examples/leftjoin.py
trunk/SQLObject/setup.py
trunk/SQLObject/sqlobject/__init__.py
trunk/SQLObject/sqlobject/dbconnection.py
trunk/SQLObject/sqlobject/main.py
Log:
Split database support into subpackages, one per driver
Added URI support, which is not the most convenient way to
specify the database. URIs can be used in lieu of a connection.
Modified: trunk/SQLObject/docs/SQLObject.txt
==============================================================================
--- trunk/SQLObject/docs/SQLObject.txt (original)
+++ trunk/SQLObject/docs/SQLObject.txt Sun Feb 8 00:39:59 2004
@@ -1,5 +1,5 @@
```````````````
-SQLObject 0.5
+SQLObject 0.6
```````````````
.. contents:: Contents:
Modified: trunk/SQLObject/examples/config.py
==============================================================================
--- trunk/SQLObject/examples/config.py (original)
+++ trunk/SQLObject/examples/config.py Sun Feb 8 00:39:59 2004
@@ -8,9 +8,14 @@
"""
## Snippet "connections"
conn = MySQLConnection(user='test', db='testdb')
+conn = 'mysql://test@localhost/testdb'
conn = PostgresConnection('user=test dbname=testdb')
+conn = 'postgres://test@localhost/testdb'
conn = SQLiteConnect('database.db')
+conn = 'sqlite://path/to/database.db'
conn = DBMConnection('database/')
+conn = 'dbm://path/to/database/'
## end snippet
"""
-conn = MySQLConnection(user='test', db='test')
+#conn = MySQLConnection(user='test', db='test')
+conn = 'mysql://test@localhost/test'
Modified: trunk/SQLObject/examples/leftjoin.py
==============================================================================
--- trunk/SQLObject/examples/leftjoin.py (original)
+++ trunk/SQLObject/examples/leftjoin.py Sun Feb 8 00:39:59 2004
@@ -1,14 +1,5 @@
from sqlobject import *
-
-## Use one of these to define your connection:
-"""
-conn = MySQLConnection(user='test', db='testdb')
-conn = PostgresConnection('user=test dbname=testdb')
-conn = SQLiteConnect('database.db')
-conn = DBMConnection('database/')
-"""
-__connection__ = MySQLConnection(user='test', db='test')
-
+from setup import *
class Customer(SQLObject):
Modified: trunk/SQLObject/setup.py
==============================================================================
--- trunk/SQLObject/setup.py (original)
+++ trunk/SQLObject/setup.py Sun Feb 8 00:39:59 2004
@@ -2,6 +2,9 @@
import warnings
warnings.filterwarnings("ignore", "Unknown distribution option")
+subpackages = ['dbm', 'firebird', 'include', 'mysql', 'postgres',
+ 'sqlite', 'sybase']
+
import sys
# patch distutils if it can't cope with the "classifiers" keyword
if sys.version < '2.2.3':
@@ -30,7 +33,7 @@
author_email="ia...@co...",
url="http://sqlobject.org",
license="LGPL",
- packages=["sqlobject", "sqlobject.include"],
+ packages=["sqlobject"] + ['sqlobject.%s' % package for package in subpackages],
download_url="http://prdownloads.sourceforge.net/sqlobject/SQLObject-0.6.tar.gz?download")
# Send announce to:
Modified: trunk/SQLObject/sqlobject/__init__.py
==============================================================================
--- trunk/SQLObject/sqlobject/__init__.py (original)
+++ trunk/SQLObject/sqlobject/__init__.py Sun Feb 8 00:39:59 2004
@@ -1,7 +1,19 @@
from main import *
from col import *
from sqlbuilder import AND, OR, NOT, IN, LIKE, CONTAINSSTRING, const, func
-from dbconnection import *
from styles import *
from joins import *
from include import validators
+
+## Each of these imports allows the driver to install itself
+
+import mysql
+del mysql
+import postgres
+del postgres
+import sqlite
+del sqlite
+import dbm
+del dbm
+import sybase
+del sybase
Modified: trunk/SQLObject/sqlobject/dbconnection.py
==============================================================================
--- trunk/SQLObject/sqlobject/dbconnection.py (original)
+++ trunk/SQLObject/sqlobject/dbconnection.py Sun Feb 8 00:39:59 2004
@@ -14,32 +14,15 @@
from joins import sorter
from converters import sqlrepr
-# We set these up as globals, which will be set if we end up
-# needing the drivers:
-anydbm = None
-pickle = None
-MySQLdb = None
-psycopg = None
-pgdb = None
-sqlite = None
-kinterbasdb = None
-Sybase = None
-
warnings.filterwarnings("ignore", "DB-API extension cursor.lastrowid used")
-__all__ = ['MySQLConnection', 'PostgresConnection', 'SQLiteConnection',
- 'DBMConnection', 'FirebirdConnection', 'SybaseConnection']
-
_connections = {}
class DBConnection:
def __init__(self, name=None, debug=False, debugOutput=False,
cache=True, style=None, autoCommit=True):
- if name:
- assert not _connections.has_key(name), 'A database by the name %s has already been created: %s' % (name, _connections[name])
- _connections[name] = self
- self.name = name
+ self.name = name
self.debug = debug
self.debugOutput = debugOutput
self.cache = CacheSet(cache=cache)
@@ -48,9 +31,52 @@
self._connectionNumbers = {}
self._connectionCount = 1
self.autoCommit = autoCommit
+ registerConnectionInstance(self)
-def connectionForName(name):
- return _connections[name]
+ def uri(self):
+ auth = self.user or ''
+ if auth:
+ if self.password:
+ auth = auth + '@' + self.password
+ auth = auth + ':'
+ else:
+ assert not password, 'URIs cannot express passwords without usernames'
+ uri = '%s://%s' % (self.dbName, auth)
+ if self.host:
+ uri += self.host + '/'
+ if path.startswith('/'):
+ path = path[1:]
+ return uri + path
+
+ def isSupported(cls):
+ raise NotImplemented
+ isSupported = classmethod(isSupported)
+
+ def connectionFromURI(cls, uri):
+ raise NotImplemented
+ connectionFromURI = classmethod(connectionFromURI)
+
+ def _parseURI(uri, expectHost=True):
+ schema, rest = uri.split(':', 1)
+ rest = rest.strip('/')
+ if expectHost:
+ if rest.find('/') == -1:
+ host, rest = rest, ''
+ else:
+ host, rest = rest.split('/', 1)
+ if host.find('@') != -1:
+ user, host = host.split('@', 1)
+ if user.find(':') != -1:
+ user, password = user.split(':', 1)
+ else:
+ password = None
+ else:
+ user = password = None
+ else:
+ host = user = password = None
+ path = '/' + rest
+ return user, password, host, path
+ _parseURI = staticmethod(_parseURI)
class DBAPI(DBConnection):
@@ -433,951 +459,44 @@
self.rollback()
self._dbConnection.releaseConnection(self._connection)
-########################################
-## MySQL connection
-########################################
-
-class MySQLConnection(DBAPI):
-
- supportTransactions = False
- dbName = 'mysql'
-
- def __init__(self, db, user, passwd='', host='localhost', **kw):
- global MySQLdb
- if MySQLdb is None:
- import MySQLdb
- self.host = host
- self.db = db
- self.user = user
- self.passwd = passwd
- DBAPI.__init__(self, **kw)
-
- def makeConnection(self):
- return MySQLdb.connect(host=self.host, db=self.db,
- user=self.user, passwd=self.passwd)
-
- def _queryInsertID(self, conn, table, idName, id, names, values):
- c = conn.cursor()
- if id is not None:
- names = [idName] + names
- values = [id] + values
- q = self._insertSQL(table, names, values)
- if self.debug:
- self.printDebug(conn, q, 'QueryIns')
- c.execute(q)
- if id is None:
- id = c.insert_id()
- if self.debugOutput:
- self.printDebug(conn, id, 'QueryIns', 'result')
- return id
-
- def _queryAddLimitOffset(self, query, start, end):
- if not start:
- return "%s LIMIT %i" % (query, end)
- if not end:
- return "%s LIMIT %i, -1" % (query, start)
- return "%s LIMIT %i, %i" % (query, start, end-start)
-
- def createColumn(self, soClass, col):
- return col.mysqlCreateSQL()
-
- def createIDColumn(self, soClass):
- return '%s INT PRIMARY KEY AUTO_INCREMENT' % soClass._idName
-
- def joinSQLType(self, join):
- return 'INT NOT NULL'
-
- def tableExists(self, tableName):
- for (table,) in self.queryAll('SHOW TABLES'):
- if table.lower() == tableName.lower():
- return True
- return False
-
- def addColumn(self, tableName, column):
- self.query('ALTER TABLE %s ADD COLUMN %s' %
- (tableName,
- column.mysqlCreateSQL()))
-
- def delColumn(self, tableName, column):
- self.query('ALTER TABLE %s DROP COLUMN %s' %
- (tableName,
- column.dbName))
-
- def columnsFromSchema(self, tableName, soClass):
- colData = self.queryAll("SHOW COLUMNS FROM %s"
- % tableName)
- results = []
- for field, t, nullAllowed, key, default, extra in colData:
- if field == 'id':
- continue
- colClass, kw = self.guessClass(t)
- kw['name'] = soClass._style.dbColumnToPythonAttr(field)
- kw['notNone'] = not nullAllowed
- kw['default'] = default
- # @@ skip key...
- # @@ skip extra...
- results.append(colClass(**kw))
- return results
-
- def guessClass(self, t):
- if t.startswith('int'):
- return col.IntCol, {}
- elif t.startswith('varchar'):
- return col.StringCol, {'length': int(t[8:-1])}
- elif t.startswith('char'):
- return col.StringCol, {'length': int(t[5:-1]),
- 'varchar': False}
- elif t.startswith('datetime'):
- return col.DateTimeCol, {}
- elif t.startswith('bool'):
- return col.BoolCol, {}
- else:
- return col.Col, {}
-
-########################################
-## Postgres connection
-########################################
-
-class PostgresConnection(DBAPI):
-
- supportTransactions = True
- dbName = 'postgres'
-
- def __init__(self, dsn=None, host=None, db=None,
- user=None, passwd=None, autoCommit=1,
- usePygresql=False,
- **kw):
- global psycopg, pgdb
- if usePygresql:
- if pgdb is None:
- import pgdb
- self.pgmodule = pgdb
- else:
- if psycopg is None:
- import psycopg
- self.pgmodule = psycopg
-
- self.autoCommit = autoCommit
- if not autoCommit and not kw.has_key('pool'):
- # Pooling doesn't work with transactions...
- kw['pool'] = 0
- if dsn is None:
- dsn = []
- if db:
- dsn.append('dbname=%s' % db)
- if user:
- dsn.append('user=%s' % user)
- if passwd:
- dsn.append('password=%s' % passwd)
- if host:
- # @@: right format?
- dsn.append('host=%s' % host)
- dsn = ' '.join(dsn)
- self.dsn = dsn
- DBAPI.__init__(self, **kw)
-
- def _setAutoCommit(self, conn, auto):
- conn.autocommit(auto)
-
- def makeConnection(self):
- conn = self.pgmodule.connect(self.dsn)
- if self.autoCommit:
- conn.autocommit(1)
- return conn
-
- def _queryInsertID(self, conn, table, idName, id, names, values):
- c = conn.cursor()
- if id is not None:
- names = [idName] + names
- values = [id] + values
- q = self._insertSQL(table, names, values)
- if self.debug:
- self.printDebug(conn, q, 'QueryIns')
- c.execute(q)
- if id is None:
- c.execute('SELECT %s FROM %s WHERE oid = %s'
- % (idName, table, c.lastoid()))
- id = c.fetchone()[0]
- if self.debugOutput:
- self.printDebug(conn, id, 'QueryIns', 'result')
- return id
-
- def _queryAddLimitOffset(self, query, start, end):
- if not start:
- return "%s LIMIT %i" % (query, end)
- if not end:
- return "%s OFFSET %i" % (query, start)
- return "%s LIMIT %i OFFSET %i" % (query, end-start, start)
-
- def createColumn(self, soClass, col):
- return col.postgresCreateSQL()
-
- def createIDColumn(self, soClass):
- return '%s SERIAL PRIMARY KEY' % soClass._idName
-
- def dropTable(self, tableName, cascade=False):
- self.query("DROP TABLE %s %s" % (tableName,
- cascade and 'CASCADE' or ''))
-
- def joinSQLType(self, join):
- return 'INT NOT NULL'
-
- def tableExists(self, tableName):
- # @@: obviously broken
- result = self.queryOne("SELECT COUNT(relname) FROM pg_class WHERE relname = '%s'"
- % tableName)
- return result[0]
-
- def addColumn(self, tableName, column):
- self.query('ALTER TABLE %s ADD COLUMN %s' %
- (tableName,
- column.postgresCreateSQL()))
-
- def delColumn(self, tableName, column):
- self.query('ALTER TABLE %s DROP COLUMN %s' %
- (tableName,
- column.dbName))
-
- def columnsFromSchema(self, tableName, soClass):
-
- keyQuery = """
- SELECT pg_catalog.pg_get_constraintdef(oid) as condef
- FROM pg_catalog.pg_constraint r
- WHERE r.conrelid = '%s'::regclass AND r.contype = 'f'"""
-
- colQuery = """
- SELECT a.attname,
- pg_catalog.format_type(a.atttypid, a.atttypmod), a.attnotnull,
- (SELECT substring(d.adsrc for 128) FROM pg_catalog.pg_attrdef d
- WHERE d.adrelid=a.attrelid AND d.adnum = a.attnum)
- FROM pg_catalog.pg_attribute a
- WHERE a.attrelid ='%s'::regclass
- AND a.attnum > 0 AND NOT a.attisdropped
- ORDER BY a.attnum"""
-
- keyData = self.queryAll(keyQuery % tableName)
- keyRE = re.compile("\((.+)\) REFERENCES (.+)\(")
- keymap = {}
- for (condef,) in keyData:
- match = keyRE.search(condef)
- if match:
- field, reftable = match.groups()
- keymap[field] = reftable.capitalize()
- colData = self.queryAll(colQuery % tableName)
- results = []
- for field, t, notnull, defaultstr in colData:
- if field == 'id':
- continue
- colClass, kw = self.guessClass(t)
- kw['name'] = soClass._style.dbColumnToPythonAttr(field)
- kw['notNone'] = notnull
- if defaultstr is not None:
- kw['default'] = getattr(sqlbuilder.const, defaultstr)
- if keymap.has_key(field):
- kw['foreignKey'] = keymap[field]
- results.append(colClass(**kw))
- return results
-
- def guessClass(self, t):
- if t.count('int'):
- return col.IntCol, {}
- elif t.count('varying'):
- return col.StringCol, {'length': int(t[t.index('(')+1:-1])}
- elif t.startswith('character('):
- return col.StringCol, {'length': int(t[t.index('(')+1:-1]),
- 'varchar': False}
- elif t == 'text':
- return col.StringCol, {}
- elif t.startswith('datetime'):
- return col.DateTimeCol, {}
- elif t.startswith('bool'):
- return col.BoolCol, {}
- else:
- return col.Col, {}
-
-
-########################################
-## SQLite connection
-########################################
-
-class SQLiteConnection(DBAPI):
-
- supportTransactions = True
- dbName = 'sqlite'
-
- def __init__(self, filename, autoCommit=1, **kw):
- global sqlite
- if sqlite is None:
- import sqlite
- self.filename = filename # full path to sqlite-db-file
- if not autoCommit and not kw.has_key('pool'):
- # Pooling doesn't work with transactions...
- kw['pool'] = 0
- # use only one connection for sqlite - supports multiple
- # cursors per connection
- self._conn = sqlite.connect(self.filename)
- DBAPI.__init__(self, **kw)
-
- def _setAutoCommit(self, conn, auto):
- conn.autocommit = auto
-
- def makeConnection(self):
- return self._conn
-
- def _queryInsertID(self, conn, table, idName, id, names, values):
- c = conn.cursor()
- if id is not None:
- names = [idName] + names
- values = [id] + values
- q = self._insertSQL(table, names, values)
- if self.debug:
- self.printDebug(conn, q, 'QueryIns')
- c.execute(q)
- # lastrowid is a DB-API extension from "PEP 0249":
- if id is None:
- id = int(c.lastrowid)
- if self.debugOutput:
- self.printDebug(conn, id, 'QueryIns', 'result')
- return id
-
- def _queryAddLimitOffset(self, query, start, end):
- if not start:
- return "%s LIMIT %i" % (query, end)
- if not end:
- return "%s LIMIT 0 OFFSET %i" % (query, start)
- return "%s LIMIT %i OFFSET %i" % (query, end-start, start)
-
- def createColumn(self, soClass, col):
- return col.sqliteCreateSQL()
-
- def createIDColumn(self, soClass):
- return '%s INTEGER PRIMARY KEY' % soClass._idName
-
- def joinSQLType(self, join):
- return 'INT NOT NULL'
-
- def tableExists(self, tableName):
- result = self.queryOne("SELECT tbl_name FROM sqlite_master WHERE type='table' AND tbl_name = '%s'" % tableName)
- # turn it into a boolean:
- return not not result
-
-########################################
-## Sybase connection
-########################################
-
-class SybaseConnection(DBAPI):
-
- supportTransactions = True
- dbName = 'sybase'
-
- def __init__(self, db, user, passwd='', host='localhost',
- autoCommit=0, **kw):
- global Sybase
- if Sybase is None:
- import Sybase
- from Sybase import NumericType
- from Converters import registerConverter, IntConverter
- registerConverter(NumericType, IntConverter)
- if not autoCommit and not kw.has_key('pool'):
- # Pooling doesn't work with transactions...
- kw['pool'] = 0
- self.autoCommit=autoCommit
- self.host = host
- self.db = db
- self.user = user
- self.passwd = passwd
- DBAPI.__init__(self, **kw)
-
- def insert_id(self, conn):
- """
- Sybase adapter/cursor does not support the
- insert_id method.
- """
- c = conn.cursor()
- c.execute('SELECT @@IDENTITY')
- return c.fetchone()[0]
-
- def makeConnection(self):
- return Sybase.connect(self.host, self.user, self.passwd,
- database=self.db, auto_commit=self.autoCommit)
-
- def _queryInsertID(self, conn, table, idName, id, names, values):
- c = conn.cursor()
- if id is not None:
- names = [idName] + names
- values = [id] + values
- c.execute('SET IDENTITY_INSERT %s ON' % table)
- else:
- c.execute('SET IDENTITY_INSERT %s OFF' % table)
- q = self._insertSQL(table, names, values)
- if self.debug:
- print 'QueryIns: %s' % q
- c.execute(q)
- if id is None:
- id = self.insert_id(conn)
- if self.debugOutput:
- self.printDebug(conn, id, 'QueryIns', 'result')
- return id
-
- def _queryAddLimitOffset(self, query, start, end):
- # XXX Sybase doesn't support LIMIT
- return query
-
- def createColumn(self, soClass, col):
- return col.sybaseCreateSQL()
-
- def createIDColumn(self, soClass):
- return '%s NUMERIC(18,0) IDENTITY' % soClass._idName
-
- def joinSQLType(self, join):
- return 'NUMERIC(18,0) NOT NULL'
-
- SHOW_TABLES="SELECT name FROM sysobjects WHERE type='U'"
- def tableExists(self, tableName):
- for (table,) in self.queryAll(self.SHOW_TABLES):
- if table.lower() == tableName.lower():
- return True
- return False
-
- def addColumn(self, tableName, column):
- self.query('ALTER TABLE %s ADD COLUMN %s' %
- (tableName,
- column.sybaseCreateSQL()))
-
- def delColumn(self, tableName, column):
- self.query('ALTER TABLE %s DROP COLUMN %s' %
- (tableName,
- column.dbName))
-
- SHOW_COLUMNS=("select 'column' = COL_NAME(id, colid) "
- "from syscolumns where id = OBJECT_ID(%s)")
- def columnsFromSchema(self, tableName, soClass):
- colData = self.queryAll(self.SHOW_COLUMNS
- % tableName)
- results = []
- for field, t, nullAllowed, key, default, extra in colData:
- if field == 'id':
- continue
- colClass, kw = self.guessClass(t)
- kw['name'] = soClass._style.dbColumnToPythonAttr(field)
- kw['notNone'] = not nullAllowed
- kw['default'] = default
- # @@ skip key...
- # @@ skip extra...
- results.append(colClass(**kw))
- return results
-
- def guessClass(self, t):
- if t.startswith('int'):
- return col.IntCol, {}
- elif t.startswith('varchar'):
- return col.StringCol, {'length': int(t[8:-1])}
- elif t.startswith('char'):
- return col.StringCol, {'length': int(t[5:-1]),
- 'varchar': False}
- elif t.startswith('datetime'):
- return col.DateTimeCol, {}
- else:
- return col.Col, {}
-
-########################################
-## Firebird connection
-########################################
-
-class FirebirdConnection(DBAPI):
-
- supportTransactions = False
- dbName = 'firebird'
-
- def __init__(self, host, db, user='sysdba',
- passwd='masterkey', autoCommit=1, **kw):
- global kinterbasdb
- if kinterbasdb is None:
- import kinterbasdb
-
- self.limit_re = re.compile('^\s*(select )(.*)', re.IGNORECASE)
-
- if not autoCommit and not kw.has_key('pool'):
- # Pooling doesn't work with transactions...
- kw['pool'] = 0
-
- self.host = host
- self.db = db
- self.user = user
- self.passwd = passwd
-
- DBAPI.__init__(self, **kw)
-
- def _runWithConnection(self, meth, *args):
- conn = self.getConnection()
- # @@: Horrible auto-commit implementation. Just horrible!
- try:
- conn.begin()
- except kinterbasdb.ProgrammingError:
- pass
- try:
- val = meth(conn, *args)
- try:
- conn.commit()
- except kinterbasdb.ProgrammingError:
- pass
- finally:
- self.releaseConnection(conn)
- return val
-
- def _setAutoCommit(self, conn, auto):
- # Only _runWithConnection does "autocommit", so we don't
- # need to worry about that.
- pass
-
- def makeConnection(self):
- return kinterbasdb.connect(
- host = self.host, database = self.db,
- user = self.user, password = self.passwd
- )
-
- def _queryInsertID(self, conn, table, idName, id, names, values):
- """Firebird uses 'generators' to create new ids for a table.
- The users needs to create a generator named GEN_<tablename>
- for each table this method to work."""
-
- if id is None:
- row = self.queryOne('SELECT gen_id(GEN_%s,1) FROM rdb$database'
- % table)
- id = row[0]
- names = [idName] + names
- values = [id] + values
- q = self._insertSQL(table, names, values)
- if self.debug:
- self.printDebug(conn, q, 'QueryIns')
- self.query(q)
- if self.debugOutput:
- self.printDebug(conn, id, 'QueryIns', 'result')
- return id
-
- def _queryAddLimitOffset(self, query, start, end):
- """Firebird slaps the limit and offset (actually 'first' and
- 'skip', respectively) statement right after the select."""
- if not start:
- limit_str = "SELECT FIRST %i" % end
- if not end:
- limit_str = "SELECT SKIP %i" % start
- else:
- limit_str = "SELECT FIRST %i SKIP %i" % (end-start, start)
-
- match = self.limit_re.match(query)
- if match and len(match.groups()) == 2:
- return ' '.join([limit_str, match.group(2)])
- else:
- return query
-
- def createTable(self, soClass):
- self.query('CREATE TABLE %s (\n%s\n)' % \
- (soClass._table, self.createColumns(soClass)))
- self.query("CREATE GENERATOR GEN_%s" % soClass._table)
-
- def createColumn(self, soClass, col):
- return col.firebirdCreateSQL()
-
- def createIDColumn(self, soClass):
- return '%s INT NOT NULL PRIMARY KEY' % soClass._idName
-
- def joinSQLType(self, join):
- return 'INT NOT NULL'
-
- def tableExists(self, tableName):
- # there's something in the database by this name...let's
- # assume it's a table. By default, fb 1.0 stores EVERYTHING
- # it cares about in uppercase.
- result = self.queryOne("SELECT COUNT(rdb$relation_name) FROM rdb$relations WHERE rdb$relation_name = '%s'"
- % tableName.upper())
- return result[0]
-
- def addColumn(self, tableName, column):
- self.query('ALTER TABLE %s ADD %s' %
- (tableName,
- column.firebirdCreateSQL()))
-
- def dropTable(self, tableName, cascade=False):
- self.query("DROP TABLE %s" % tableName)
- self.query("DROP GENERATOR GEN_%s" % tableName)
-
- def delColumn(self, tableName, column):
- self.query('ALTER TABLE %s DROP %s' %
- (tableName,
- column.dbName))
-
- def columnsFromSchema(self, tableName, soClass):
- """
- Look at the given table and create Col instances (or
- subclasses of Col) for the fields it finds in that table.
- """
-
- fieldqry = """\
- SELECT RDB$RELATION_FIELDS.RDB$FIELD_NAME as field,
- RDB$TYPES.RDB$TYPE_NAME as t,
- RDB$FIELDS.RDB$FIELD_LENGTH as flength,
- RDB$FIELDS.RDB$FIELD_SCALE as fscale,
- RDB$RELATION_FIELDS.RDB$NULL_FLAG as nullAllowed,
- RDB$RELATION_FIELDS.RDB$DEFAULT_VALUE as thedefault,
- RDB$FIELDS.RDB$FIELD_SUB_TYPE as blobtype
- FROM RDB$RELATION_FIELDS
- INNER JOIN RDB$FIELDS ON
- (RDB$RELATION_FIELDS.RDB$FIELD_SOURCE = RDB$FIELDS.RDB$FIELD_NAME)
- INNER JOIN RDB$TYPES ON (RDB$FIELDS.RDB$FIELD_TYPE =
- RDB$TYPES.RDB$TYPE)
- WHERE
- (RDB$RELATION_FIELDS.RDB$RELATION_NAME = '%s')
- AND (RDB$TYPES.RDB$FIELD_NAME = 'RDB$FIELD_TYPE')"""
-
- colData = self.queryAll(fieldqry % tableName.upper())
- results = []
- for field, t, flength, fscale, nullAllowed, thedefault, blobType in colData:
- if field == 'id':
- continue
- colClass, kw = self.guessClass(t, flength, fscale)
- kw['name'] = soClass._style.dbColumnToPythonAttr(field)
- kw['notNone'] = not nullAllowed
- kw['default'] = thedefault
- results.append(colClass(**kw))
- return results
-
- _intTypes=['INT64', 'SHORT','LONG']
- _dateTypes=['DATE','TIME','TIMESTAMP']
-
- def guessClass(self, t, flength, fscale=None):
- """
- An internal method that tries to figure out what Col subclass
- is appropriate given whatever introspective information is
- available -- both very database-specific.
- """
-
- if t in self._intTypes:
- return col.IntCol, {}
- elif t == 'VARYING':
- return col.StringCol, {'length': flength}
- elif t == 'TEXT':
- return col.StringCol, {'length': flength,
- 'varchar': False}
- elif t in self._dateTypes:
- return col.DateTimeCol, {}
- else:
- return col.Col, {}
-
-########################################
-## File-based connections
-########################################
-
-class FileConnection(DBConnection):
-
- """
- Files connections should deal with setup, and define the
- methods:
-
- * ``_fetchDict(self, table, id)``
- * ``_saveDict(self, table, id, d)``
- * ``_newID(table)``
- * ``tableExists(table)``
- * ``createTable(soClass)``
- * ``dropTable(table)``
- * ``clearTable(table)``
- * ``_SO_delete(so)``
- * ``_allIDs()``
- * ``_SO_createJoinTable(join)``
- """
-
- def queryInsertID(self, table, idName, id, names, values):
- if id is None:
- id = self._newID(table)
- self._saveDict(table, id, dict(zip(names, values)))
- return id
-
- def createColumns(self, soClass):
- pass
-
- def _SO_update(self, so, values):
- d = self._fetchDict(so._table, so.id)
- for dbName, value in values:
- d[dbName] = value
- self._saveDict(so._table, so.id, d)
-
- def _SO_selectOne(self, so, columnNames):
- d = self._fetchDict(so._table, so.id)
- return [d[name] for name in columnNames]
-
- def _SO_selectOneAlt(self, cls, columnNames, column, value):
- for id in self._allIDs(cls._table):
- d = self._fetchDict(cls._table, id)
- if d[column] == value:
- d['id'] = id
- return [d[name] for name in columnNames]
-
- _createRE = re.compile('CREATE TABLE\s+(IF NOT EXISTS\s+)?([^ ]*)', re.I)
- _dropRE = re.compile('DROP TABLE\s+(IF EXISTS\s+)?([^ ]*)', re.I)
-
- def query(self, q):
- match = self._createRE.search(q)
- if match:
- if match.group(1) and self.tableExists(match.group(2)):
- return
- class X: pass
- x = X()
- x._table = match.group(2)
- return self.createTable(x)
- match = self._dropRE.search(q)
- if match:
- if match.group(1) and not self.tableExists(match.group(2)):
- return
- return self.dropTable(match.group(2))
-
- def addColumn(self, tableName, column):
- for id in self._allIDs(tableName):
- d = self._fetchDict(tableName, id)
- d[column.dbName] = None
- self._saveDict(tableName, id, d)
-
- def delColumn(self, tableName, column):
- for id in self._allIDs(tableName):
- d = self._fetchDict(tableName, id)
- del d[column.dbName]
- self._saveDict(tableName, id, d)
-
- def _SO_columnClause(self, soClass, kw):
- clauses = []
- for name, value in kw.items():
- clauses.append(getattr(soClass.q, name) == value)
- return sqlbuilder.AND(*clauses)
-
- def _SO_selectJoin(self, soClass, column, value):
- results = []
- # @@: seems lame I need to do this...
- value = int(value)
- for id in self._allIDs(soClass._table):
- d = self._fetchDict(soClass._table, id)
- if d[column] == value:
- results.append((id,))
- return results
-
-########################################
-## DBM connection
-########################################
-
-class DBMConnection(FileConnection):
-
- supportTransactions = False
- dbName = 'dbm'
-
- def __init__(self, path, **kw):
- global anydbm, pickle
- if anydbm is None:
- import anydbm
- if pickle is None:
- try:
- import cPickle as pickle
- except ImportError:
- import pickle
- self.path = path
- try:
- self._meta = anydbm.open(os.path.join(path, "meta.db"), "w")
- except anydbm.error:
- self._meta = anydbm.open(os.path.join(path, "meta.db"), "c")
- self._tables = {}
- atexit.register(self.close)
- self._closed = 0
- FileConnection.__init__(self, **kw)
-
- def _newID(self, table):
- id = int(self._meta["%s.id" % table]) + 1
- self._meta["%s.id" % table] = str(id)
- return id
-
- def _saveDict(self, table, id, d):
- db = self._getDB(table)
- db[str(id)] = pickle.dumps(d)
-
- def _fetchDict(self, table, id):
- return pickle.loads(self._getDB(table)[str(id)])
-
- def _getDB(self, table):
- try:
- return self._tables[table]
- except KeyError:
- db = self._openTable(table)
- self._tables[table] = db
- return db
-
- def close(self):
- if self._closed:
- return
- self._closed = 1
- self._meta.close()
- del self._meta
- for table in self._tables.values():
- table.close()
- del self._tables
-
- def __del__(self):
- FileConnection.__del__(self)
- self.close()
-
- def _openTable(self, table):
- try:
- db = anydbm.open(os.path.join(self.path, "%s.db" % table), "w")
- except anydbm.error:
- db = anydbm.open(os.path.join(self.path, "%s.db" % table), "c")
- return db
-
- def tableExists(self, table):
- return self._meta.has_key("%s.id" % table) \
- or os.path.exists(os.path.join(self.path, table + ".db"))
-
- def createTable(self, soClass):
- self._meta["%s.id" % soClass._table] = "1"
-
- def dropTable(self, tableName, cascade=False):
- try:
- del self._meta["%s.id" % tableName]
- except KeyError:
- pass
- self.clearTable(tableName)
-
- def clearTable(self, tableName):
- if self._tables.has_key(tableName):
- del self._tables[tableName]
- filename = os.path.join(self.path, "%s.db" % tableName)
- if os.path.exists(filename):
- os.unlink(filename)
-
- def _SO_delete(self, so):
- db = self._getDB(so._table)
- del db[str(so.id)]
-
- def iterSelect(self, select):
- return DBMSelectResults(self, select)
-
- def _allIDs(self, tableName):
- return self._getDB(tableName).keys()
-
- def _SO_createJoinTable(self, join):
- pass
-
- def _SO_dropJoinTable(self, join):
- os.unlink(os.path.join(self.path, join.intermediateTable + ".db"))
-
- def _SO_intermediateJoin(self, table, get, join1, id1):
- db = self._openTable(table)
- try:
- results = db[join1 + str(id1)]
- except KeyError:
- return []
- if not results:
- return []
- return [(int(id),) for id in results.split(',')]
-
- def _SO_intermediateInsert(self, table, join1, id1, join2, id2):
- db = self._openTable(table)
- try:
- results = db[join1 + str(id1)]
- except KeyError:
- results = ""
- if results:
- db[join1 + str(id1)] = results + "," + str(id2)
- else:
- db[join1 + str(id1)] = str(id2)
-
- try:
- results = db[join2 + str(id2)]
- except KeyError:
- results = ""
- if results:
- db[join2 + str(id2)] = results + "," + str(id1)
- else:
- db[join2 + str(id2)] = str(id1)
-
- def _SO_intermediateDelete(self, table, join1, id1, join2, id2):
- db = self._openTable(table)
- try:
- results = db[join1 + str(id1)]
- except KeyError:
- results = ""
- results = map(int, results.split(","))
- results.remove(int(id2))
- db[join1 + str(id1)] = ",".join(map(str, results))
- try:
- results = db[join2 + str(id2)]
- except KeyError:
- results = ""
- results = map(int, results.split(","))
- results.remove(int(id1))
- db[join2 + str(id2)] = ",".join(map(str, results))
-
-class DBMSelectResults(object):
-
- def __init__(self, conn, select):
- self.select = select
- self.conn = conn
- self.tables = select.tables
- self.tableDict = {}
- self._results = None
- for i in range(len(self.tables)):
- self.tableDict[self.tables[i]] = i
- self.comboIter = _iterAllCombinations(
- [self.conn._getDB(table).keys() for table in self.tables])
- if select.ops.get('orderBy'):
- self._maxNext = -1
- results = self.allResults()
- results.sort(sorter(select.ops['orderBy']))
- self._results = results
- if select.ops.get('start'):
- if select.ops.get('end'):
- self._results = self._results[select.ops['start']:select.ops['end']]
- else:
- self._results = self._results[select.ops['start']:]
- elif select.ops.get('end'):
- self._results = self._results[:select.ops['end']]
- elif select.ops.get('start'):
- for i in range(select.ops.get('start')):
- self.next()
- if select.ops.get('end'):
- self._maxNext = select.ops['end'] - select.ops['start']
- elif select.ops.get('end'):
- self._maxNext = select.ops['end']
- else:
- self._maxNext = -1
-
- def next(self):
- if self._results is not None:
- try:
- return self._results.pop(0)
- except IndexError:
- raise StopIteration
-
- for idList in self.comboIter:
- self.idList = idList
- if sqlbuilder.execute(self.select.clause, self):
- if not self._maxNext:
- raise StopIteration
- self._maxNext -= 1
- return self.select.sourceClass.get(int(idList[self.tableDict[self.select.sourceClass._table]]))
- raise StopIteration
-
- def field(self, table, field):
- return self.conn._fetchDict(table, self.idList[self.tableDict[table]])[field]
-
- def allResults(self):
- results = []
- while 1:
- try:
- results.append(self.next())
- except StopIteration:
- return results
-
+class ConnectionURIOpener(object):
-def _iterAllCombinations(l):
- if len(l) == 1:
- for id in l[0]:
- yield [id]
- else:
- for id in l[0]:
- for idList in _iterAllCombinations(l[1:]):
- yield [id] + idList
+ def __init__(self):
+ self.allClasses = []
+ self.classSchemes = {}
+ self.instanceNames = {}
+
+ def registerConnectionClass(self, cls):
+ if cls not in self.allClasses:
+ self.allClasses.append(cls)
+ for uriScheme in cls.schemes:
+ assert not self.classSchemes.has_key(uriScheme) \
+ or self.classSchemes[uriScheme] is cls, \
+ "A class has already been registered for the URI scheme %s" % uriScheme
+ self.classSchemes[uriScheme] = cls
+
+ def registerConnectionInstance(self, inst):
+ if inst.name:
+ assert not self.instanceNames.has_key(inst.name) \
+ or self.instanceNames[inst.name] is cls, \
+ "A instance has already been registered with the name %s" % inst.name
+ assert inst.name.find(':') == -1, "You cannot include ':' in your class names (%r)" % cls.name
+ self.instanceNames[inst.name] = inst
+
+ def openURI(self, uri):
+ if uri.find(':') != -1:
+ scheme, rest = uri.split(':', 1)
+ assert self.classSchemes.has_key(scheme), \
+ "No SQLObject driver exists for %s" % scheme
+ return self.classSchemes[scheme].connectionFromURI(uri)
+ else:
+ # We just have a name, not a URI
+ assert self.instanceNames.has_key(uri), \
+ "No SQLObject driver exists under the name %s" % uri
+ return self.instanceNames[uri]
+
+TheURIOpener = ConnectionURIOpener()
+
+registerConnectionClass = TheURIOpener.registerConnectionClass
+registerConnectionInstance = TheURIOpener.registerConnectionInstance
+openURI = TheURIOpener.openURI
Added: trunk/SQLObject/sqlobject/dbm/__init__.py
==============================================================================
--- (empty file)
+++ trunk/SQLObject/sqlobject/dbm/__init__.py Sun Feb 8 00:39:59 2004
@@ -0,0 +1,5 @@
+from sqlobject import dbconnection
+from dbmconnection import DBMConnection
+
+dbconnection.registerConnectionClass(
+ DBMConnection)
Added: trunk/SQLObject/sqlobject/dbm/dbmconnection.py
==============================================================================
--- (empty file)
+++ trunk/SQLObject/sqlobject/dbm/dbmconnection.py Sun Feb 8 00:39:59 2004
@@ -0,0 +1,343 @@
+from sqlobject.dbconnection import DBConnection
+import re
+anydbm = None
+pickle = None
+
+########################################
+## File-based connections
+########################################
+
+class FileConnection(DBConnection):
+
+ """
+ Files connections should deal with setup, and define the
+ methods:
+
+ * ``_fetchDict(self, table, id)``
+ * ``_saveDict(self, table, id, d)``
+ * ``_newID(table)``
+ * ``tableExists(table)``
+ * ``createTable(soClass)``
+ * ``dropTable(table)``
+ * ``clearTable(table)``
+ * ``_SO_delete(so)``
+ * ``_allIDs()``
+ * ``_SO_createJoinTable(join)``
+ """
+
+ def queryInsertID(self, table, idName, id, names, values):
+ if id is None:
+ id = self._newID(table)
+ self._saveDict(table, id, dict(zip(names, values)))
+ return id
+
+ def createColumns(self, soClass):
+ pass
+
+ def _SO_update(self, so, values):
+ d = self._fetchDict(so._table, so.id)
+ for dbName, value in values:
+ d[dbName] = value
+ self._saveDict(so._table, so.id, d)
+
+ def _SO_selectOne(self, so, columnNames):
+ d = self._fetchDict(so._table, so.id)
+ return [d[name] for name in columnNames]
+
+ def _SO_selectOneAlt(self, cls, columnNames, column, value):
+ for id in self._allIDs(cls._table):
+ d = self._fetchDict(cls._table, id)
+ if d[column] == value:
+ d['id'] = id
+ return [d[name] for name in columnNames]
+
+ _createRE = re.compile('CREATE TABLE\s+(IF NOT EXISTS\s+)?([^ ]*)', re.I)
+ _dropRE = re.compile('DROP TABLE\s+(IF EXISTS\s+)?([^ ]*)', re.I)
+
+ def query(self, q):
+ match = self._createRE.search(q)
+ if match:
+ if match.group(1) and self.tableExists(match.group(2)):
+ return
+ class X: pass
+ x = X()
+ x._table = match.group(2)
+ return self.createTable(x)
+ match = self._dropRE.search(q)
+ if match:
+ if match.group(1) and not self.tableExists(match.group(2)):
+ return
+ return self.dropTable(match.group(2))
+
+ def addColumn(self, tableName, column):
+ for id in self._allIDs(tableName):
+ d = self._fetchDict(tableName, id)
+ d[column.dbName] = None
+ self._saveDict(tableName, id, d)
+
+ def delColumn(self, tableName, column):
+ for id in self._allIDs(tableName):
+ d = self._fetchDict(tableName, id)
+ del d[column.dbName]
+ self._saveDict(tableName, id, d)
+
+ def _SO_columnClause(self, soClass, kw):
+ clauses = []
+ for name, value in kw.items():
+ clauses.append(getattr(soClass.q, name) == value)
+ return sqlbuilder.AND(*clauses)
+
+ def _SO_selectJoin(self, soClass, column, value):
+ results = []
+ # @@: seems lame I need to do this...
+ value = int(value)
+ for id in self._allIDs(soClass._table):
+ d = self._fetchDict(soClass._table, id)
+ if d[column] == value:
+ results.append((id,))
+ return results
+
+########################################
+## DBM connection
+########################################
+
+class DBMConnection(FileConnection):
+
+ supportTransactions = False
+ dbName = 'dbm'
+ schemes = [dbName]
+
+ def __init__(self, path, **kw):
+ global anydbm, pickle
+ if anydbm is None:
+ import anydbm
+ if pickle is None:
+ try:
+ import cPickle as pickle
+ except ImportError:
+ import pickle
+ self.path = path
+ try:
+ self._meta = anydbm.open(os.path.join(path, "meta.db"), "w")
+ except anydbm.error:
+ self._meta = anydbm.open(os.path.join(path, "meta.db"), "c")
+ self._tables = {}
+ atexit.register(self.close)
+ self._closed = 0
+ self.host = self.user = self.password = None
+ FileConnection.__init__(self, **kw)
+
+ def connectionFromURI(cls, uri):
+ user, password, host, path = self._parseURI(uri, expectHost=False)
+ assert host is None
+ assert user is None and password is None, \
+ "SQLite cannot accept usernames or passwords"
+ path = '/' + path
+ return cls(path)
+ connectionFromURI = classmethod(connectionFromURI)
+
+ def isSupported(cls):
+ global anydbm
+ if anydbm is None:
+ try:
+ import anydbm
+ except ImportError:
+ return False
+ return True
+ isSupported = classmethod(isSupported)
+
+ def _newID(self, table):
+ id = int(self._meta["%s.id" % table]) + 1
+ self._meta["%s.id" % table] = str(id)
+ return id
+
+ def _saveDict(self, table, id, d):
+ db = self._getDB(table)
+ db[str(id)] = pickle.dumps(d)
+
+ def _fetchDict(self, table, id):
+ return pickle.loads(self._getDB(table)[str(id)])
+
+ def _getDB(self, table):
+ try:
+ return self._tables[table]
+ except KeyError:
+ db = self._openTable(table)
+ self._tables[table] = db
+ return db
+
+ def close(self):
+ if self._closed:
+ return
+ self._closed = 1
+ self._meta.close()
+ del self._meta
+ for table in self._tables.values():
+ table.close()
+ del self._tables
+
+ def __del__(self):
+ FileConnection.__del__(self)
+ self.close()
+
+ def _openTable(self, table):
+ try:
+ db = anydbm.open(os.path.join(self.path, "%s.db" % table), "w")
+ except anydbm.error:
+ db = anydbm.open(os.path.join(self.path, "%s.db" % table), "c")
+ return db
+
+ def tableExists(self, table):
+ return self._meta.has_key("%s.id" % table) \
+ or os.path.exists(os.path.join(self.path, table + ".db"))
+
+ def createTable(self, soClass):
+ self._meta["%s.id" % soClass._table] = "1"
+
+ def dropTable(self, tableName, cascade=False):
+ try:
+ del self._meta["%s.id" % tableName]
+ except KeyError:
+ pass
+ self.clearTable(tableName)
+
+ def clearTable(self, tableName):
+ if self._tables.has_key(tableName):
+ del self._tables[tableName]
+ filename = os.path.join(self.path, "%s.db" % tableName)
+ if os.path.exists(filename):
+ os.unlink(filename)
+
+ def _SO_delete(self, so):
+ db = self._getDB(so._table)
+ del db[str(so.id)]
+
+ def iterSelect(self, select):
+ return DBMSelectResults(self, select)
+
+ def _allIDs(self, tableName):
+ return self._getDB(tableName).keys()
+
+ def _SO_createJoinTable(self, join):
+ pass
+
+ def _SO_dropJoinTable(self, join):
+ os.unlink(os.path.join(self.path, join.intermediateTable + ".db"))
+
+ def _SO_intermediateJoin(self, table, get, join1, id1):
+ db = self._openTable(table)
+ try:
+ results = db[join1 + str(id1)]
+ except KeyError:
+ return []
+ if not results:
+ return []
+ return [(int(id),) for id in results.split(',')]
+
+ def _SO_intermediateInsert(self, table, join1, id1, join2, id2):
+ db = self._openTable(table)
+ try:
+ results = db[join1 + str(id1)]
+ except KeyError:
+ results = ""
+ if results:
+ db[join1 + str(id1)] = results + "," + str(id2)
+ else:
+ db[join1 + str(id1)] = str(id2)
+
+ try:
+ results = db[join2 + str(id2)]
+ except KeyError:
+ results = ""
+ if results:
+ db[join2 + str(id2)] = results + "," + str(id1)
+ else:
+ db[join2 + str(id2)] = str(id1)
+
+ def _SO_intermediateDelete(self, table, join1, id1, join2, id2):
+ db = self._openTable(table)
+ try:
+ results = db[join1 + str(id1)]
+ except KeyError:
+ results = ""
+ results = map(int, results.split(","))
+ results.remove(int(id2))
+ db[join1 + str(id1)] = ",".join(map(str, results))
+ try:
+ results = db[join2 + str(id2)]
+ except KeyError:
+ results = ""
+ results = map(int, results.split(","))
+ results.remove(int(id1))
+ db[join2 + str(id2)] = ",".join(map(str, results))
+
+class DBMSelectResults(object):
+
+ def __init__(self, conn, select):
+ self.select = select
+ self.conn = conn
+ self.tables = select.tables
+ self.tableDict = {}
+ self._results = None
+ for i in range(len(self.tables)):
+ self.tableDict[self.tables[i]] = i
+ self.comboIter = _iterAllCombinations(
+ [self.conn._getDB(table).keys() for table in self.tables])
+ if select.ops.get('orderBy'):
+ self._maxNext = -1
+ results = self.allResults()
+ results.sort(sorter(select.ops['orderBy']))
+ self._results = results
+ if select.ops.get('start'):
+ if select.ops.get('end'):
+ self._results = self._results[select.ops['start']:select.ops['end']]
+ else:
+ self._results = self._results[select.ops['start']:]
+ elif select.ops.get('end'):
+ self._results = self._results[:select.ops['end']]
+ elif select.ops.get('start'):
+ for i in range(select.ops.get('start')):
+ self.next()
+ if select.ops.get('end'):
+ self._maxNext = select.ops['end'] - select.ops['start']
+ elif select.ops.get('end'):
+ self._maxNext = select.ops['end']
+ else:
+ self._maxNext = -1
+
+ def next(self):
+ if self._results is not None:
+ try:
+ return self._results.pop(0)
+ except IndexError:
+ raise StopIteration
+
+ for idList in self.comboIter:
+ self.idList = idList
+ if sqlbuilder.execute(self.select.clause, self):
+ if not self._maxNext:
+ raise StopIteration
+ self._maxNext -= 1
+ return self.select.sourceClass.get(int(idList[self.tableDict[self.select.sourceClass._table]]))
+ raise StopIteration
+
+ def field(self, table, field):
+ return self.conn._fetchDict(table, self.idList[self.tableDict[table]])[field]
+
+ def allResults(self):
+ results = []
+ while 1:
+ try:
+ results.append(self.next())
+ except StopIteration:
+ return results
+
+
+def _iterAllCombinations(l):
+ if len(l) == 1:
+ for id in l[0]:
+ yield [id]
+ else:
+ for id in l[0]:
+ for idList in _iterAllCombinations(l[1:]):
+ yield [id] + idList
Added: trunk/SQLObject/sqlobject/firebird/__init__.py
==============================================================================
--- (empty file)
+++ trunk/SQLObject/sqlobject/firebird/__init__.py Sun Feb 8 00:39:59 2004
@@ -0,0 +1,5 @@
+from sqlobject import dbconnection
+from firebirdconnection import FirebirdConnection
+
+dbconnection.registerConnectionClass(
+ FirebirdConnection)
Added: trunk/SQLObject/sqlobject/firebird/firebirdconnection.py
==============================================================================
--- (empty file)
+++ trunk/SQLObject/sqlobject/firebird/firebirdconnection.py Sun Feb 8 00:39:59 2004
@@ -0,0 +1,202 @@
+from sqlobject.dbconnection import DBAPI
+kinterbasdb = None
+
+class FirebirdConnection(DBAPI):
+
+ supportTransactions = False
+ dbName = 'firebird'
+ schemes = [dbName]
+
+ def __init__(self, host, db, user='sysdba',
+ passwd='masterkey', autoCommit=1, **kw):
+ global kinterbasdb
+ if kinterbasdb is None:
+ import kinterbasdb
+
+ self.limit_re = re.compile('^\s*(select )(.*)', re.IGNORECASE)
+
+ if not autoCommit and not kw.has_key('pool'):
+ # Pooling doesn't work with transactions...
+ kw['pool'] = 0
+
+ self.host = host
+ self.db = db
+ self.user = user
+ self.passwd = passwd
+
+ DBAPI.__init__(self, **kw)
+
+ def connectionFromURI(cls, uri):
+ auth, password, host, path = self._parseURI(url)
+ if not password:
+ password = 'masterkey'
+ if not auth:
+ auth='sysdba'
+ return cls(host, db=path, user=user, passwd=password)
+ connectionFromURI = classmethod(connectionFromURI)
+
+ def isSupported(cls):
+ global kinterbasdb
+ if kinterbasdb is None:
+ try:
+ import kinterbasdb
+ except ImportError:
+ return False
+ return True
+ isSupported = classmethod(isSupported)
+
+ def _runWithConnection(self, meth, *args):
+ conn = self.getConnection()
+ # @@: Horrible auto-commit implementation. Just horrible!
+ try:
+ conn.begin()
+ except kinterbasdb.ProgrammingError:
+ pass
+ try:
+ val = meth(conn, *args)
+ try:
+ conn.commit()
+ except kinterbasdb.ProgrammingError:
+ pass
+ finally:
+ self.releaseConnection(conn)
+ return val
+
+ def _setAutoCommit(self, conn, auto):
+ # Only _runWithConnection does "autocommit", so we don't
+ # need to worry about that.
+ pass
+
+ def makeConnection(self):
+ return kinterbasdb.connect(
+ host = self.host, database = self.db,
+ user = self.user, password = self.passwd
+ )
+
+ def _queryInsertID(self, conn, table, idName, id, names, values):
+ """Firebird uses 'generators' to create new ids for a table.
+ The users needs to create a generator named GEN_<tablename>
+ for each table this method to work."""
+
+ if id is None:
+ row = self.queryOne('SELECT gen_id(GEN_%s,1) FROM rdb$database'
+ % table)
+ id = row[0]
+ names = [idName] + names
+ values = [id] + values
+ q = self._insertSQL(table, names, values)
+ if self.debug:
+ self.printDebug(conn, q, 'QueryIns')
+ self.query(q)
+ if self.debugOutput:
+ self.printDebug(conn, id, 'QueryIns', 'result')
+ return id
+
+ def _queryAddLimitOffset(self, query, start, end):
+ """Firebird slaps the limit and offset (actually 'first' and
+ 'skip', respectively) statement right after the select."""
+ if not start:
+ limit_str = "SELECT FIRST %i" % end
+ if not end:
+ limit_str = "SELECT SKIP %i" % start
+ else:
+ limit_str = "SELECT FIRST %i SKIP %i" % (end-start, start)
+
+ match = self.limit_re.match(query)
+ if match and len(match.groups()) == 2:
+ return ' '.join([limit_str, match.group(2)])
+ else:
+ return query
+
+ def createTable(self, soClass):
+ self.query('CREATE TABLE %s (\n%s\n)' % \
+ (soClass._table, self.createColumns(soClass)))
+ self.query("CREATE GENERATOR GEN_%s" % soClass._table)
+
+ def createColumn(self, soClass, col):
+ return col.firebirdCreateSQL()
+
+ def createIDColumn(self, soClass):
+ return '%s INT NOT NULL PRIMARY KEY' % soClass._idName
+
+ def joinSQLType(self, join):
+ return 'INT NOT NULL'
+
+ def tableExists(self, tableName):
+ # there's something in the database by this name...let's
+ # assume it's a table. By default, fb 1.0 stores EVERYTHING
+ # it cares about in uppercase.
+ result = self.queryOne("SELECT COUNT(rdb$relation_name) FROM rdb$relations WHERE rdb$relation_name = '%s'"
+ % tableName.upper())
+ return result[0]
+
+ def addColumn(self, tableName, column):
+ self.query('ALTER TABLE %s ADD %s' %
+ (tableName,
+ column.firebirdCreateSQL()))
+
+ def dropTable(self, tableName, cascade=False):
+ self.query("DROP TABLE %s" % tableName)
+ self.query("DROP GENERATOR GEN_%s" % tableName)
+
+ def delColumn(self, tableName, column):
+ self.query('ALTER TABLE %s DROP %s' %
+ (tableName,
+ column.dbName))
+
+ def columnsFromSchema(self, tableName, soClass):
+ """
+ Look at the given table and create Col instances (or
+ subclasses of Col) for the fields it finds in that table.
+ """
+
+ fieldqry = """\
+ SELECT RDB$RELATION_FIELDS.RDB$FIELD_NAME as field,
+ RDB$TYPES.RDB$TYPE_NAME as t,
+ RDB$FIELDS.RDB$FIELD_LENGTH as flength,
+ RDB$FIELDS.RDB$FIELD_SCALE as fscale,
+ RDB$RELATION_FIELDS.RDB$NULL_FLAG as nullAllowed,
+ RDB$RELATION_FIELDS.RDB$DEFAULT_VALUE as thedefault,
+ RDB$FIELDS.RDB$FIELD_SUB_TYPE as blobtype
+ FROM RDB$RELATION_FIELDS
+ INNER JOIN RDB$FIELDS ON
+ (RDB$RELATION_FIELDS.RDB$FIELD_SOURCE = RDB$FIELDS.RDB$FIELD_NAME)
+ INNER JOIN RDB$TYPES ON (RDB$FIELDS.RDB$FIELD_TYPE =
+ RDB$TYPES.RDB$TYPE)
+ WHERE
+ (RDB$RELATION_FIELDS.RDB$RELATION_NAME = '%s')
+ AND (RDB$TYPES.RDB$FIELD_NAME = 'RDB$FIELD_TYPE')"""
+
+ colData = self.queryAll(fieldqry % tableName.upp...
[truncated message content] |
|
From: <sub...@co...> - 2004-02-07 23:31:50
|
Author: ianb Date: Sat Feb 7 14:24:34 2004 New Revision: 7 Modified: trunk/SQLObject/docs/News.txt Log: Added some news for 0.6 Modified: trunk/SQLObject/docs/News.txt ============================================================================== --- trunk/SQLObject/docs/News.txt (original) +++ trunk/SQLObject/docs/News.txt Sat Feb 7 14:24:34 2004 @@ -7,6 +7,22 @@ .. _start: +SQLObject 0.6 +============= + +Interface Changes +----------------- + +* The ``SQLObject`` package has been renamed to ``sqlobject``. This + makes it similar to several other packages, and emphasizes the + distinction between the ``sqlobject`` package and the ``SQLObject`` + class. +* Class instantiation now creates new rows (like `.new()` used to + do), and the `.get()` method now retrieves objects that already have + rows (like class instantiation used to do). +* We're now using a Subversion repository instead of CVS. It is + located at svn://colorstudy.com/trunk/SQLObject + SQLObject 0.5.2 =============== |
|
From: <sub...@co...> - 2004-02-07 23:29:15
|
Author: ianb Date: Sat Feb 7 14:21:53 2004 New Revision: 6 Modified: trunk/SQLObject/docs/SQLObject.txt trunk/SQLObject/examples/ (props changed) trunk/SQLObject/examples/codebits.py trunk/SQLObject/examples/config.py trunk/SQLObject/examples/examplestripper.py trunk/SQLObject/examples/leftjoin.py trunk/SQLObject/examples/people.py trunk/SQLObject/examples/personaddress.py trunk/SQLObject/examples/setup.py trunk/SQLObject/examples/simpleperson.py trunk/SQLObject/examples/styles.py trunk/SQLObject/examples/userrole.py Log: Fixed examples and documentation for .new/.get change, and the new sqlobject (vs. SQLObject) package Modified: trunk/SQLObject/docs/SQLObject.txt ============================================================================== --- trunk/SQLObject/docs/SQLObject.txt (original) +++ trunk/SQLObject/docs/SQLObject.txt Sat Feb 7 14:21:53 2004 @@ -65,7 +65,7 @@ effects (it changes the database), and defining classes has side effects (through the use of metaclasses). Attributes are generally exposed, not marked private, knowing that they can be made dynamic -later. +or write-only later. SQLObject creates objects that feel similar to normal Python objects (with the semantics of new-style classes). An attribute attached to a @@ -96,10 +96,10 @@ .. _Webware: http://webware.sourceforge.net SQLObject provides a strong database abstraction, allowing -cross-database compatibility (so long as you don't specifically go -around SQLObject). This compatibility extends not just to several -databases, but also to currently one non-SQL, non-relational backend -(based on the `dbm` module). +cross-database compatibility (so long as you don't sidestep +SQLObject). This compatibility extends not just to several databases, +but also to currently one non-SQL, non-relational backend (based on +the `dbm` module). SQLObject has joins, one-to-many, and many-to-many, something which many ORMs do not have. The join system is also intended to be @@ -109,7 +109,7 @@ names; often these two won't match, or the database style would be inappropriate for a Python attribute. This way your database schema does not have to be designed with SQLObject in mind, and the resulting -classes +classes do not have to inherit the database's naming schemes. Future ====== @@ -130,6 +130,10 @@ * More kinds of joins, and more powerful join results (closer to how `select` works). +See also the `Plan for 0.6`__. + +.. __: Plan06.html + Using SQLObject: An Introduction ================================ @@ -164,6 +168,11 @@ use (you can also set a module-level variable `__connection__` which would automatically be picked up if you don't specify `_connection`). +.. warning:: + The `__connection__` magic variable can be a little fragile -- it + has to be defined before the class is defined. This means it + *must* be assigned above the ``class ...:`` line. + `firstName`, `middleInitial`, and `lastName` are all columns in the database. The general schema implied by this class definition is: @@ -191,7 +200,7 @@ you want to create the tables yourself), you can just use the vague `Col` class. SQLObject doesn't do much type checking, allowing the database and the adapter to handle most of the type conversion. -Databases usually do their own type coercion anyway. +Databases generally do their own type coercion on inputs. You'll note that the ``id`` column is not given in the class definition, it is implied. For MySQL databases it should be defined @@ -209,16 +218,23 @@ Now that you have a class, how will you use it? We'll be considering the class defined above. -You can use the standard constructor to fetch instances that *already -exist*. So if you wanted to fetch the Person by id 10, you'd call -``Person(10)``. - -To create a new object (and row), use the `.new()` class method. In -this case you might call ``Person.new(firstName="John", -lastName="Doe")``. If you had left out ``firstName`` or ``lastName`` -you would have gotten an error, as no default was given for these -columns (``middleInitial`` has a default, so it will be set to -``NULL``, the SQL equivalent of ``None``). +You can use the class method `.get()` to fetch instances that +already exist. So if you wanted to fetch the Person by id 10, you'd +call ``Person.get(10)``. + +.. warning:: + This is a change from SQLObject 0.5 -- before the standard + constructor fetched rows from the database, and the `.new()` + method created new rows. Now SQLObject is more like Python, where + the class constructor creates a new object/row, and the `.get()` + method fetches a row. + +To create a new object (and row), use class instantiation. In this +case you might call ``Person.new(firstName="John", lastName="Doe")``. +If you had left out ``firstName`` or ``lastName`` you would have +gotten an error, as no default was given for these columns +(``middleInitial`` has a default, so it will be set to ``NULL``, the +SQL equivalent of ``None``). When you create an object, it is immediately inserted into the database. SQLObject generally uses the database as immediate storage. @@ -236,7 +252,7 @@ person by a particular ID, you'll get back the same instance. This way you can be sure of a certain amount of consistency if you have multiple threads accessing the same data (though of course across -processes there can be no sharing of an instance). This changes if +processes there can be no sharing of an instance). This isn't true if you're using transactions_. To get an idea of what's happening behind the surface, I'll give the @@ -259,15 +275,15 @@ :file: ../examples/snippets/simpleaddress-person1-use-set.html This will send only one ``UPDATE`` statement. You can also use `set` -with non-database properties (there's no benefit, but the distinction -between database columns and other attributes thus remains somewhat -hidden). +with non-database properties (there's no benefit, but it helps hide +the difference between database and non-database attributes). + One-to-Many Relationships ------------------------- -Well, a real address book should have people, but also addresses. -These examples are in ``personaddress.py`` +A real address book should have people, but also addresses. These +examples are in ``personaddress.py`` First, let's define the new address table. People can have multiple addresses, of course: @@ -275,7 +291,7 @@ .. raw:: html :file: ../examples/snippets/address-address.html -Note the column ``person = ForeignKey('Person')``. This is a +Note the column ``person = ForeignKey("Person")``. This is a reference to a `Person` object. We refer to other classes by name (with a string) to avoid circular dependencies. In the database there will be a ``person_id`` column, type ``INT``, which points to @@ -286,10 +302,10 @@ .. raw:: html :file: ../examples/snippets/address-person.html -We get the backreference with ``addresses = -MultipleJoin('Address')``. When we access a person's `addresses` -attribute, we will get back a (dynamic) list of all the `Address` -objects associated with that person. An example: +We get the backreference with ``addresses = MultipleJoin('Address')``. +When we access a person's `addresses` attribute, we will get back a +list of all the `Address` objects associated with that person. An +example: .. raw:: html :file: ../examples/snippets/address-use1.html @@ -320,6 +336,17 @@ identifier is in addition to the primary key (``id``), which is always present. +.. note:: + SQLObject has a strong requirement that the primary key be unique + and *immutable*. You cannot change the primary key through + SQLObject, and if you change it through another mechanism you can + cause inconsistency in any running SQLObject program (and in your + data). For this reason meaningless integer IDs are encouraged -- + something like a username that could change in the future may + uniquely identify a row, but it may be changed in the future. So + long as it is not used to reference the row internally, it is also + *safe* to change it in the future. + A alternateID column creates a class method, like ``byUsername`` for a column named ``username`` (or you can use the `alternateMethodName` keyword argument to override this). Its use: @@ -356,8 +383,8 @@ .. raw:: html :file: ../examples/snippets/person-select3.html -You may wish to use `MyClass.sqlrepr` to quote any values you use -if you use this technique (quoting is automatic if you use ``q``). +You may wish to use `MyClass.sqlrepr` to quote any values you use if +you create SQL manually (quoting is automatic if you use ``q``). Tables given in `clauseTables` will be added to the ``FROM`` portion (again, they are automatically picked up when using ``q``). The table you're selecting is always assumed to be included, of course. @@ -367,9 +394,8 @@ You can use the keyword arguments `orderBy` to create ``ORDER BY`` in the select statements: `orderBy` takes a string, which should be the *database* name of the column, or a column in the form -``Person.q.firstName``; `groupBy` is similar. Both accept lists or -tuples of arguments. You can use ``"-colname"`` to specify descending -order, or call ``MyClass.select().reversed()``. +``Person.q.firstName``. You can use ``"-colname"`` to specify +descending order, or call ``MyClass.select().reversed()``. You can use the special class variable `_defaultOrder` to give a default ordering for all selects. To get an unordered result when @@ -380,7 +406,9 @@ use ``list()`` to force the result to be executed. When you iterate over the select results, rows are fetched one at a time. This way you can iterate over large results without keeping the entire result set -in memory. +in memory. You can also do things like ``.reversed()`` without +fetching and reversing the entire result -- instead, SQLObject can +change the SQL that is sent so you get equivalent results. You can also slice select results. The results are used in the SQL query, so ``peeps[:10]`` will result in ``LIMIT 10`` being added to @@ -398,6 +426,29 @@ .. raw:: html :file: ../examples/snippets/slicing-batch.html +.. note:: + + There are several factors when considering the efficiency of this + kind of batching, and it depends very much how the batching is + being used. Consider a web application where you are showing an + average of 100 results, 10 at a time, and the results are ordered + by the date they were added to the database. While slicing will + keep the database from returning all the results (and so save some + communication time), the database will still have to scan through + the entire result set to sort the items (so it knows which the + first ten are), and depending on your query may need to scan + through the entire table (depending on your use of indexes). + Indexes are probably the most important way to improve importance + in a case like this, and you may find caching to be more effective + than slicing. + + In this case, caching would mean retrieving the *complete* results. + You can use ``list(MyClass.select(...))`` to do this. You can save + these results for some limited period of time, as the user looks + through the results page by page. This means the first page in a + search result will be slightly more expensive, but all later pages + will be very cheap. + For more information on the where clause in the queries, see the `SQLBuilder documentation`_. @@ -414,29 +465,36 @@ Initializing the Objects ~~~~~~~~~~~~~~~~~~~~~~~~ -With new-style classes, `__init__` is called everytime the class is -called. That means it's called when an object is just fetched from -the cache. That's useless in most cases, so instead we use a `_init` -method, which is only called once in an object's life (with one -argument -- the object's ID). +There are two ways SQLObject instances can come into existance: they +can be fetched from the database, or they can be inserted into the +database. In both cases a new Python object is created. This makes +the place of `__init__` a little confusing. + +In general, you should not touch `__init__`. Instead use the `_init` +method, which is called after an object is fetched or inserted. This +method has the signature ``_init(self, id, connection=None, +selectResults=None)``, though you may just want to use ``_init(self, +*args, **kw)``. Adding Magic Attributes (properties) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -You can use all the normal techniques for defining this new-style -class, including `classmethod`, `staticmethod`, and `property`, but -you can use a shortcut. If you have a method that's name starts with -``_set_``, ``_get_``, ``_del_``, or ``_doc_``, it will be used to -create a property. So, for instance, say you have images stored under -the ID of the person in the ``/var/people/images`` directory: +You can use all the normal techniques for defining methods in this +new-style class, including `classmethod`, `staticmethod`, and +`property`, but you can also use a shortcut. If you have a method +that's name starts with ``_set_``, ``_get_``, ``_del_``, or ``_doc_``, +it will be used to create a property. So, for instance, say you have +images stored under the ID of the person in the ``/var/people/images`` +directory: .. raw:: html :file: ../examples/snippets/person_magicmethod.html Later, you can use the ``.image`` property just like an attribute, and the changes will be reflected in the filesystem by calling these -methods. I use this particular technique frequently for information -that is better to keep in files as opposed to the database. +methods. This is a good technique for information that is better to +keep in files as opposed to the database (such as large, opaque data +like images). You can also pass an ``image`` keyword argument to the `new` class method or the `set` method, like ``Person.new(..., image=imageText)``. @@ -455,12 +513,13 @@ code you want to run whenever someone's name changes -- you could make a subclass, and then use ``Person.__setattr__(self, 'lastName', value)`` to actually do the deed, but that's obviously very awkward -- -you have to create subclasses without an real inheritance -relationship, and the whole thing feels architecturally fragile. -SQLObject creates methods like ``_set_lastName`` for each of your -columns, but again you can't use this, since there's no superclass to -reference (and you can't write ``SQLObject._set_lastName(...)``). You -want to override that ``_set_lastName`` method yourself. +you have to create subclasses without a real inheritance relationship, +and the whole thing feels architecturally fragile. SQLObject creates +methods like ``_set_lastName`` for each of your columns, but again you +can't use this, since there's no superclass to reference (and you +can't write ``SQLObject._set_lastName(...)``, because the SQLObject +class doesn't know about your class's columns). You want to override +that ``_set_lastName`` method yourself. To deal with this, SQLObject creates two methods for each getter and setter, for example: ``_set_lastName`` and ``_SO_set_lastName``. So @@ -475,12 +534,15 @@ .. raw:: html :file: ../examples/snippets/phonenumber_magicoverride.html -Of course, the user may be surprised if the value they set the -attribute to is not the same value they get back -- in this case we -removed some of the characters before putting it in the database, and -then formatted the number into a nice string on the way out. This is -one disadvantage of making actual work look like simple attribute -access. +.. note:: + + You should be a little cautious when modifying data that gets set + in an attribute. Generally someone using your class will expect + that the value they set the attribute to will be the same value + they get back. In this example we removed some of the characters + before putting it in the database, and reformatted it on the way + out. One advantage of methods (as opposed to attribute access) is + that the programmer is more likely to expect this disconnect. Reference ========= Modified: trunk/SQLObject/examples/codebits.py ============================================================================== --- trunk/SQLObject/examples/codebits.py (original) +++ trunk/SQLObject/examples/codebits.py Sat Feb 7 14:21:53 2004 @@ -67,7 +67,7 @@ ## Snippet "transactions1" conn = DBConnection.PostgresConnection('yada') trans = conn.transaction() -p = Person(1, trans) +p = Person.get(1, trans) p.firstName = 'Bob' trans.commit() p.firstName = 'Billy' Modified: trunk/SQLObject/examples/config.py ============================================================================== --- trunk/SQLObject/examples/config.py (original) +++ trunk/SQLObject/examples/config.py Sat Feb 7 14:21:53 2004 @@ -1,4 +1,4 @@ -from SQLObject import * +from sqlobject import * """ This contains basic configuration for all the examples. Since they all require a connection, you can configure that just in this file. @@ -6,10 +6,11 @@ ## Use one of these to define your connection: """ +## Snippet "connections" conn = MySQLConnection(user='test', db='testdb') conn = PostgresConnection('user=test dbname=testdb') conn = SQLiteConnect('database.db') conn = DBMConnection('database/') +## end snippet """ -conn = DBMConnection('database/') conn = MySQLConnection(user='test', db='test') Modified: trunk/SQLObject/examples/examplestripper.py ============================================================================== --- trunk/SQLObject/examples/examplestripper.py (original) +++ trunk/SQLObject/examples/examplestripper.py Sat Feb 7 14:21:53 2004 @@ -10,6 +10,10 @@ ## End Snippet Then a file snippets/snippetname.html is created. + +This isn't an example, but it's a tool for merging the examples and +the documentation. This requires the presence of the source-highlight +program: http://www.gnu.org/software/src-highlite/source-highlight.html """ import re, os, sys Modified: trunk/SQLObject/examples/leftjoin.py ============================================================================== --- trunk/SQLObject/examples/leftjoin.py (original) +++ trunk/SQLObject/examples/leftjoin.py Sat Feb 7 14:21:53 2004 @@ -1,4 +1,4 @@ -from SQLObject import * +from sqlobject import * ## Use one of these to define your connection: """ @@ -34,9 +34,9 @@ for insert in data: firstName, lastName = insert[0].split(' ', 1) - customer = Customer.new(firstName=firstName, lastName=lastName) + customer = Customer(firstName=firstName, lastName=lastName) for number in insert[1:]: - contact = Contact.new(customer=customer, phoneNumber=number) + contact = Contact(customer=customer, phoneNumber=number) ## Snippet "leftjoin-simple" for customer in Customer.select(): Modified: trunk/SQLObject/examples/people.py ============================================================================== --- trunk/SQLObject/examples/people.py (original) +++ trunk/SQLObject/examples/people.py Sat Feb 7 14:21:53 2004 @@ -1,31 +1,9 @@ #!/usr/bin/env python -from SQLObject import * +from sqlobject import * import os -############################################################ -## Configuration parameters: -############################################################ - -user = os.environ.get('SQLOBJECT_USER', 'sqlobject_test') -passwd = os.environ.get('SQLOBJECT_PASSWORD', '') -database = os.environ.get('SQLOBJECT_DATABASE', 'sqlobject_test') -debug = 1 - -############################################################ -## Setup connections: -############################################################ - -print 'Accessing with user %s and password %s' % (user, passwd) - -if 1: - # do MySQL test - __connection__ = MySQLConnection('localhost', database, - user, passwd, debug=debug) -else: - # do Postgres test - __connection__ = PostgresConnection('dbname=%s user=%s' % - (database, user), debug=debug) - +from setup import * +__connection__ = conn ############################################################ ## Define classes: @@ -93,7 +71,7 @@ if 'create' in args: for table in tableClasses: - table.createTable(ifExists=True) + table.createTable(ifNotExists=True) if 'clear' in args: for table in tableClasses: @@ -105,7 +83,7 @@ ############################################################ test1 = """ ->>> p = Person.new(firstName="John", lastName="Doe", username="johnd") +>>> p = Person(firstName="John", lastName="Doe", username="johnd") >>> print p <Person 1 firstName='John' middleInitial=None lastName='Doe'> >>> print p.firstName @@ -113,7 +91,7 @@ >>> p.middleInitial = 'Q' >>> print p.middleInitial Q ->>> p2 = Person(p.id) +>>> p2 = Person.get(p.id) >>> print p2 <Person 1 firstName='John' middleInitial='Q' lastName='Doe'> >>> print p is p2 @@ -124,7 +102,7 @@ """ test2 = """ ->>> r = Role.new(name="editor") +>>> r = Role(name="editor") >>> p = list(Person.select('all'))[-1] >>> p.addRole(r) >>> print p.roles @@ -134,7 +112,7 @@ >>> r.removePerson(p) >>> print p.roles [] ->>> phone = PhoneNumber.new(person=p, phoneNumber='773-555-1023', phoneType='home') +>>> phone = PhoneNumber(person=p, phoneNumber='773-555-1023', phoneType='home') >>> print p.phoneNumbers """ Modified: trunk/SQLObject/examples/personaddress.py ============================================================================== --- trunk/SQLObject/examples/personaddress.py (original) +++ trunk/SQLObject/examples/personaddress.py Sat Feb 7 14:21:53 2004 @@ -1,4 +1,4 @@ -from SQLObject import * +from sqlobject import * from config import conn __connection__ = conn @@ -32,21 +32,21 @@ reset() ## Snippet "address-use1" -p = Person.new(firstName='John', lastName='Doe') +p = Person(firstName='John', lastName='Doe') print p.addresses #>> [] -a1 = Address.new(street='123', city='Smallsville', +a1 = Address(street='123', city='Smallsville', state='IL', zip='50484', person=p) print [a.street for a in p.addresses] #>> ['123'] ## end snippet # We'll add some more data to make the results more interesting: -add1 = Person.new(firstName='Jane', lastName='Doe') -add2 = Person.new(firstName='Tom', lastName='Brown') -Address.new(street='5839', city='Eckersville', +add1 = Person(firstName='Jane', lastName='Doe') +add2 = Person(firstName='Tom', lastName='Brown') +Address(street='5839', city='Eckersville', state='IL', zip='50482', person=add1) -Address.new(street='4', city='Whinging', +Address(street='4', city='Whinging', state='AZ', zip='49378', person=add2) ## Snippet "person-select1" Modified: trunk/SQLObject/examples/setup.py ============================================================================== --- trunk/SQLObject/examples/setup.py (original) +++ trunk/SQLObject/examples/setup.py Sat Feb 7 14:21:53 2004 @@ -1,6 +1,6 @@ import sys from config import conn -import SQLObject +import sqlobject main = sys.modules['__main__'] @@ -12,7 +12,7 @@ for name in dir(main): value = getattr(main, name) if isinstance(value, type) \ - and issubclass(value, SQLObject.SQLObject)\ - and value is not SQLObject.SQLObject: + and issubclass(value, sqlobject.SQLObject)\ + and value is not sqlobject.SQLObject: value.dropTable(ifExists=True) value.createTable() Modified: trunk/SQLObject/examples/simpleperson.py ============================================================================== --- trunk/SQLObject/examples/simpleperson.py (original) +++ trunk/SQLObject/examples/simpleperson.py Sat Feb 7 14:21:53 2004 @@ -1,16 +1,7 @@ -from SQLObject import * +from sqlobject import * -## Use one of these to define your connection: -""" -## Snippet "connections" -conn = MySQLConnection(user='test', db='testdb') -conn = PostgresConnection('user=test dbname=testdb') -conn = SQLiteConnect('database.db') -conn = DBMConnection('database/') -## End snippet -""" -conn = DBMConnection('database/') -conn = MySQLConnection(user='test', db='test') +from setup import * +__connection__ = conn ## Snippet "simpleaddress-person1" class Person(SQLObject): @@ -47,7 +38,7 @@ ## End snippet ## Snippet "simpleaddress-person1-use" -p = Person.new(firstName="John", lastName="Doe") +p = Person(firstName="John", lastName="Doe") print p #>> <Person 1 firstName='John' middleInitial=None lastName='Doe'> print p.firstName @@ -55,7 +46,7 @@ p.middleInitial = 'Q' print p.middleInitial #>> 'Q' -p2 = Person(1) +p2 = Person.get(1) print p2 #>> <Person 1 firstName='John' middleInitial='Q' lastName='Doe'> print p is p2 @@ -67,7 +58,7 @@ conn.debug = 1 ## Snippet "simpleaddress-person1-use-debug" -p = Person.new(firstName="John", lastName="Doe") +p = Person(firstName="John", lastName="Doe") #>> QueryIns: # INSERT INTO person (last_name, middle_initial, first_name) # VALUES ('Doe', NULL, 'John') @@ -91,7 +82,7 @@ # WHERE id = 1 print p.middleInitial #>> 'Q' -p2 = Person(1) +p2 = Person.get(1) #-- Again, no database access, since we're just grabbing the same #-- instance we already had. print p2 Modified: trunk/SQLObject/examples/styles.py ============================================================================== --- trunk/SQLObject/examples/styles.py (original) +++ trunk/SQLObject/examples/styles.py Sat Feb 7 14:21:53 2004 @@ -1,4 +1,4 @@ -from SQLObject import * +from sqlobject import * from config import conn __connection__ = conn Modified: trunk/SQLObject/examples/userrole.py ============================================================================== --- trunk/SQLObject/examples/userrole.py (original) +++ trunk/SQLObject/examples/userrole.py Sat Feb 7 14:21:53 2004 @@ -1,4 +1,4 @@ -from SQLObject import * +from sqlobject import * import setup __connection__ = setup.conn @@ -34,12 +34,12 @@ setup.reset() ## Snippet "userrole-use" -bob = User.new(username='bob') -tim = User.new(username='tim') -jay = User.new(username='jay') +bob = User(username='bob') +tim = User(username='tim') +jay = User(username='jay') -admin = Role.new(name='admin') -editor = Role.new(name='editor') +admin = Role(name='admin') +editor = Role(name='editor') bob.addRole(admin) bob.addRole(editor) |
|
From: <sub...@co...> - 2004-02-07 23:28:03
|
Author: ianb
Date: Sat Feb 7 14:20:47 2004
New Revision: 5
Modified:
trunk/SQLObject/docs/default.css
Log:
Made notes and warnings display more nicely.
Modified: trunk/SQLObject/docs/default.css
==============================================================================
--- trunk/SQLObject/docs/default.css (original)
+++ trunk/SQLObject/docs/default.css Sat Feb 7 14:20:47 2004
@@ -53,9 +53,10 @@
// margin: 2em ;
background-color: #cccccc;
align: center;
- //width: 60%;
- // border: medium outset ;
+ width: 40%;
+ border: medium outset ;
padding: 3px;
+ float: right;
}
div.attention p.admonition-title, div.caution p.admonition-title,
@@ -64,13 +65,19 @@
color: red ;
font-weight: bold ;
font-family: sans-serif;
- text-align: center }
+ text-align: center;
+ display: block;
+ background-color: #999999;
+ margin: 0; }
div.hint p.admonition-title, div.important p.admonition-title,
div.note p.admonition-title, div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif;
- text-align: center }
+ text-align: center;
+ display: block;
+ background-color: #999999;
+ margin: 0; }
div.dedication {
margin: 2em 5em ;
|
|
From: <sub...@co...> - 2004-02-07 23:27:51
|
Author: ianb
Date: Sat Feb 7 14:20:30 2004
New Revision: 4
Modified:
trunk/SQLObject/docs/Plan06.txt
Log:
Fixed some reST formatting problems
Modified: trunk/SQLObject/docs/Plan06.txt
==============================================================================
--- trunk/SQLObject/docs/Plan06.txt (original)
+++ trunk/SQLObject/docs/Plan06.txt Sat Feb 7 14:20:30 2004
@@ -116,36 +116,45 @@
explicit ways to control things like:
1. Caching of instances:
- + Application/process-global definition.
- + Database-level definition.
- + Transaction/EditingContext-level definition.
- + Class-level definition.
+
+ * Application/process-global definition.
+ * Database-level definition.
+ * Transaction/EditingContext-level definition.
+ * Class-level definition.
+
2. Caching of columns:
- + Class-level.
+
+ * Class-level.
+
3. Cache sweep frequency:
- + Application/process-global.
- + Database-level.
- + Class-level.
- + Doesn't need to be as complete as 1; maybe on the class level you
+
+ * Application/process-global.
+ * Database-level.
+ * Class-level.
+ * Doesn't need to be as complete as 1; maybe on the class level you
could only indicate that a certain class should not be sweeped.
- + Sweep during a fetch (e.g., every 100 fetches), by time or fetch
+ * Sweep during a fetch (e.g., every 100 fetches), by time or fetch
frequency, or sweep with an explicit call (e.g., to do sweeps in
a separate thread).
+
4. Cache sweep policy:
- + Maximum age.
- + Least-recently-used (actually, least-recently-fetched).
- + Random (the current policy).
- + Multi-level (randomly move objects to a lower-priority cache,
+
+ * Maximum age.
+ * Least-recently-used (actually, least-recently-fetched).
+ * Random (the current policy).
+ * Multi-level (randomly move objects to a lower-priority cache,
raise level when the object is fetched again).
- + Target cache size (keep trimming until the cache is small
+ * Target cache size (keep trimming until the cache is small
enough).
- + Simple policy (if enough objects qualify, cache can be of any
+ * Simple policy (if enough objects qualify, cache can be of any
size).
- + Percentage culling (e.g., kill 33% of objects for each sweep;
+ * Percentage culling (e.g., kill 33% of objects for each sweep;
this is the current policy).
+
5. Batching of updates (whether updates should immediately go to the
database, or whether it would be batched until a commit or other
signal).
+
6. Natural expiring of objects. Even if an object must persist
because there are still references, we could expire it so that
future accesses re-query the database. To avoid stale data.
|
|
From: <sub...@co...> - 2004-02-06 06:17:06
|
Author: ianb
Date: Thu Feb 5 21:09:52 2004
New Revision: 3
Modified:
trunk/SQLObject/sqlobject/dbconnection.py
trunk/SQLObject/sqlobject/joins.py
trunk/SQLObject/sqlobject/main.py
trunk/SQLObject/tests/ (props changed)
trunk/SQLObject/tests/test.py
Log:
Renamed __new__ to get, renamed new to __init__
(in other words, class instantiation creates a row, while the
get() class method fetches an object)
Modified: trunk/SQLObject/sqlobject/dbconnection.py
==============================================================================
--- trunk/SQLObject/sqlobject/dbconnection.py (original)
+++ trunk/SQLObject/sqlobject/dbconnection.py Thu Feb 5 21:09:52 2004
@@ -176,10 +176,10 @@
self.releaseConnection(conn)
break
if select.ops.get('lazyColumns', 0):
- obj = select.sourceClass(result[0], connection=withConnection)
+ obj = select.sourceClass.get(result[0], connection=withConnection)
yield obj
else:
- obj = select.sourceClass(result[0], selectResults=result[1:], connection=withConnection)
+ obj = select.sourceClass.get(result[0], selectResults=result[1:], connection=withConnection)
yield obj
def iterSelect(self, select):
@@ -1358,7 +1358,7 @@
if not self._maxNext:
raise StopIteration
self._maxNext -= 1
- return self.select.sourceClass(int(idList[self.tableDict[self.select.sourceClass._table]]))
+ return self.select.sourceClass.get(int(idList[self.tableDict[self.select.sourceClass._table]]))
raise StopIteration
def field(self, table, field):
Modified: trunk/SQLObject/sqlobject/joins.py
==============================================================================
--- trunk/SQLObject/sqlobject/joins.py (original)
+++ trunk/SQLObject/sqlobject/joins.py Thu Feb 5 21:09:52 2004
@@ -115,7 +115,7 @@
conn = inst._connection
else:
conn = None
- return self._applyOrderBy([self.otherClass(id, conn) for (id,) in ids if id is not None], self.otherClass)
+ return self._applyOrderBy([self.otherClass.get(id, conn) for (id,) in ids if id is not None], self.otherClass)
class MultipleJoin(Join):
baseClass = SOMultipleJoin
@@ -155,7 +155,7 @@
conn = inst._connection
else:
conn = None
- return self._applyOrderBy([self.otherClass(id, conn) for (id,) in ids if id is not None], self.otherClass)
+ return self._applyOrderBy([self.otherClass.get(id, conn) for (id,) in ids if id is not None], self.otherClass)
def remove(self, inst, other):
inst._connection._SO_intermediateDelete(
Modified: trunk/SQLObject/sqlobject/main.py
==============================================================================
--- trunk/SQLObject/sqlobject/main.py (original)
+++ trunk/SQLObject/sqlobject/main.py Thu Feb 5 21:09:52 2004
@@ -309,26 +309,10 @@
# when necessary: (bad clever? maybe)
_expired = False
- def __new__(cls, id, connection=None, selectResults=None):
+ def get(cls, id, connection=None, selectResults=None):
assert id is not None, 'None is not a possible id for %s' % cls.__name
- # When id is CreateNewSQLObject, that means we are trying to
- # create a new object. This is a contract of sorts with the
- # `new()` method.
- if id is CreateNewSQLObject:
- # Create an actual new object:
- inst = object.__new__(cls)
- inst._SO_creating = True
- inst._SO_validatorState = SQLObjectState(inst)
- # This is a dictionary of column-names to
- # column-values for the new row:
- inst._SO_createValues = {}
- if connection is not None:
- inst._connection = connection
- assert selectResults is None
- return inst
-
# Some databases annoyingly return longs for INT
if isinstance(id, long):
id = int(id)
@@ -343,7 +327,7 @@
val = cache.get(id, cls)
if val is None:
try:
- val = object.__new__(cls)
+ val = cls(_SO_fetch_no_create=1)
val._SO_validatorState = SQLObjectState(val)
val._init(id, connection, selectResults)
cache.put(id, cls, val)
@@ -351,6 +335,8 @@
cache.finishPut(cls)
return val
+ get = classmethod(get)
+
def addColumn(cls, columnDef, changeSchema=False):
column = columnDef.withClass(cls)
name = column.name
@@ -755,22 +741,25 @@
if id is None:
return None
elif self._SO_perConnection:
- return joinClass(id, connection=self._connection)
+ return joinClass.get(id, connection=self._connection)
else:
- return joinClass(id)
-
- def new(cls, **kw):
- # This is what creates a new row, plus the new Python
- # object to go with it.
+ return joinClass.get(id)
+ def __init__(self, **kw):
+ # The get() classmethod/constructor uses a magic keyword
+ # argument when it wants an empty object, fetched from the
+ # database. So we have nothing more to do in that case:
+ if kw.has_key('_SO_fetch_no_create'):
+ return
+
# Pass the connection object along if we were given one.
# Passing None for the ID tells __new__ we want to create
# a new object.
if kw.has_key('connection'):
- inst = cls(CreateNewSQLObject, connection=kw['connection'])
+ self._connection = kw['connection']
+ self._SO_perConnection = True
del kw['connection']
- else:
- inst = cls(CreateNewSQLObject)
+ self._SO_writeLock = threading.Lock()
if kw.has_key('id'):
id = kw['id']
@@ -778,9 +767,13 @@
else:
id = None
+ self._SO_creating = True
+ self._SO_createValues = {}
+ self._SO_validatorState = SQLObjectState(self)
+
# First we do a little fix-up on the keywords we were
# passed:
- for column in inst._SO_columns:
+ for column in self._SO_columns:
# If a foreign key is given, we get the ID of the object
# and put that in instead
@@ -805,27 +798,25 @@
forDB = {}
others = {}
for name, value in kw.items():
- if name in inst._SO_plainSetters:
+ if name in self._SO_plainSetters:
forDB[name] = value
else:
others[name] = value
# We take all the straight-to-DB values and use set() to
# set them:
- inst.set(**forDB)
+ self.set(**forDB)
# The rest go through setattr():
for name, value in others.items():
try:
- getattr(cls, name)
+ getattr(self.__class__, name)
except AttributeError:
- raise TypeError, "%s.new() got an unexpected keyword argument %s" % (cls.__name__, name)
- setattr(inst, name, value)
+ raise TypeError, "%s.new() got an unexpected keyword argument %s" % (self.__class__.__name__, name)
+ setattr(self, name, value)
# Then we finalize the process:
- inst._SO_finishCreate(id)
- return inst
- new = classmethod(new)
+ self._SO_finishCreate(id)
def _SO_finishCreate(self, id=None):
# Here's where an INSERT is finalized.
@@ -863,9 +854,9 @@
if not result:
raise SQLObjectNotFound, "The %s by alternateID %s=%s does not exist" % (cls.__name__, dbIDName, repr(value))
if connection:
- obj = cls(result[0], connection=connection)
+ obj = cls.get(result[0], connection=connection)
else:
- obj = cls(result[0])
+ obj = cls.get(result[0])
if not obj._cacheValues:
obj._SO_writeLock.acquire()
try:
Modified: trunk/SQLObject/tests/test.py
==============================================================================
--- trunk/SQLObject/tests/test.py (original)
+++ trunk/SQLObject/tests/test.py Thu Feb 5 21:09:52 2004
@@ -46,7 +46,7 @@
def inserts(self):
for name, passwd in self.info:
- self.MyClass.new(name=name, passwd=passwd)
+ self.MyClass(name=name, passwd=passwd)
def testGet(self):
bob = self.MyClass.selectBy(name='bob')[0]
@@ -96,7 +96,7 @@
classes = [Student]
def testBoolCol(self):
- student = Student.new(is_smart = False)
+ student = Student(is_smart=False)
self.assertEqual(student.is_smart, False)
class TestCase34(SQLObjectTest):
@@ -104,30 +104,30 @@
classes = [TestSO3, TestSO4]
def testForeignKey(self):
- tc3 = TestSO3.new(name='a')
+ tc3 = TestSO3(name='a')
self.assertEqual(tc3.other, None)
self.assertEqual(tc3.other2, None)
self.assertEqual(tc3.otherID, None)
self.assertEqual(tc3.other2ID, None)
- tc4a = TestSO4.new(me='1')
+ tc4a = TestSO4(me='1')
tc3.other = tc4a
self.assertEqual(tc3.other, tc4a)
self.assertEqual(tc3.otherID, tc4a.id)
- tc4b = TestSO4.new(me='2')
+ tc4b = TestSO4(me='2')
tc3.other = tc4b.id
self.assertEqual(tc3.other, tc4b)
self.assertEqual(tc3.otherID, tc4b.id)
- tc4c = TestSO4.new(me='3')
+ tc4c = TestSO4(me='3')
tc3.other2 = tc4c
self.assertEqual(tc3.other2, tc4c)
self.assertEqual(tc3.other2ID, tc4c.id)
- tc4d = TestSO4.new(me='4')
+ tc4d = TestSO4(me='4')
tc3.other2 = tc4d.id
self.assertEqual(tc3.other2, tc4d)
self.assertEqual(tc3.other2ID, tc4d.id)
- tcc = TestSO3.new(name='b', other=tc4a)
+ tcc = TestSO3(name='b', other=tc4a)
self.assertEqual(tcc.other, tc4a)
- tcc2 = TestSO3.new(name='c', other=tc4a.id)
+ tcc2 = TestSO3(name='c', other=tc4a.id)
self.assertEqual(tcc2.other, tc4a)
class TestSO5(SQLObject):
@@ -147,10 +147,10 @@
classes = [TestSO7, TestSO6, TestSO5]
def testForeignKeyDestroySelfCascade(self):
- tc5 = TestSO5.new(name='a')
- tc6a = TestSO6.new(name='1')
+ tc5 = TestSO5(name='a')
+ tc6a = TestSO6(name='1')
tc5.other = tc6a
- tc7a = TestSO7.new(name='2')
+ tc7a = TestSO7(name='2')
tc6a.other = tc7a
tc5.another = tc7a
self.assertEqual(tc5.other, tc6a)
@@ -165,9 +165,9 @@
self.assertEqual(TestSO5.select().count(), 1)
self.assertEqual(TestSO6.select().count(), 1)
self.assertEqual(TestSO7.select().count(), 1)
- tc6b = TestSO6.new(name='3')
- tc6c = TestSO6.new(name='4')
- tc7b = TestSO7.new(name='5')
+ tc6b = TestSO6(name='3')
+ tc6c = TestSO6(name='4')
+ tc7b = TestSO7(name='5')
tc6b.other = tc7b
tc6c.other = tc7b
self.assertEqual(TestSO5.select().count(), 1)
@@ -187,15 +187,15 @@
self.assertEqual(TestSO7.select().count(), 0)
def testForeignKeyDropTableCascade(self):
- tc5a = TestSO5.new(name='a')
- tc6a = TestSO6.new(name='1')
+ tc5a = TestSO5(name='a')
+ tc6a = TestSO6(name='1')
tc5a.other = tc6a
- tc7a = TestSO7.new(name='2')
+ tc7a = TestSO7(name='2')
tc6a.other = tc7a
tc5a.another = tc7a
- tc5b = TestSO5.new(name='b')
- tc5c = TestSO5.new(name='c')
- tc6b = TestSO6.new(name='3')
+ tc5b = TestSO5(name='b')
+ tc5c = TestSO5(name='c')
+ tc6b = TestSO6(name='3')
tc5c.other = tc6b
self.assertEqual(TestSO5.select().count(), 3)
self.assertEqual(TestSO6.select().count(), 2)
@@ -210,7 +210,7 @@
self.assertEqual(TestSO5.select().count(), 1)
self.assertEqual(TestSO6.select().count(), 0)
self.assertEqual(iter(TestSO5.select()).next(), tc5b)
- tc6c = TestSO6.new(name='3')
+ tc6c = TestSO6(name='3')
tc5b.other = tc6c
self.assertEqual(TestSO5.select().count(), 1)
self.assertEqual(TestSO6.select().count(), 1)
@@ -230,11 +230,11 @@
classes = [TestSO9, TestSO8]
def testForeignKeyDestroySelfRestrict(self):
- tc8a = TestSO8.new(name='a')
- tc9a = TestSO9.new(name='1')
+ tc8a = TestSO8(name='a')
+ tc9a = TestSO9(name='1')
tc8a.other = tc9a
- tc8b = TestSO8.new(name='b')
- tc9b = TestSO9.new(name='2')
+ tc8b = TestSO8(name='b')
+ tc9b = TestSO9(name='2')
self.assertEqual(tc8a.other, tc9a)
self.assertEqual(tc8a.otherID, tc9a.id)
self.assertEqual(TestSO8.select().count(), 2)
@@ -270,7 +270,7 @@
for fname, lname in [('aj', 'baker'), ('joe', 'robbins'),
('tim', 'jackson'), ('joe', 'baker'),
('zoe', 'robbins')]:
- Names.new(fname=fname, lname=lname)
+ Names(fname=fname, lname=lname)
def testDefaultOrder(self):
self.assertEqual([(n.fname, n.lname) for n in Names.select()],
@@ -300,7 +300,7 @@
def inserts(self):
for name in self.names:
- IterTest.new(name=name)
+ IterTest(name=name)
def test_00_normal(self):
count = 0
@@ -379,15 +379,15 @@
classes = [TestSOTrans]
def inserts(self):
- TestSOTrans.new(name='bob')
- TestSOTrans.new(name='tim')
+ TestSOTrans(name='bob')
+ TestSOTrans(name='tim')
def testTransaction(self):
if not self.supportTransactions: return
trans = TestSOTrans._connection.transaction()
try:
TestSOTrans._connection.autoCommit = 'exception'
- TestSOTrans.new(name='joe', connection=trans)
+ TestSOTrans(name='joe', connection=trans)
trans.rollback()
self.assertEqual([n.name for n in TestSOTrans.select(connection=trans)],
['bob', 'tim'])
@@ -418,12 +418,12 @@
def inserts(self):
for l in ['a', 'bcd', 'a', 'e']:
- Enum1.new(l=l)
+ Enum1(l=l)
def testBad(self):
if self.supportRestrictedEnum:
try:
- v = Enum1.new(l='b')
+ v = Enum1(l='b')
except Exception, e:
pass
else:
@@ -447,7 +447,7 @@
def inserts(self):
for i in range(100):
- Counter.new(number=i)
+ Counter(number=i)
def counterEqual(self, counters, value):
self.assertEquals([c.number for c in counters], value)
@@ -492,7 +492,7 @@
def inserts(self):
for i in range(10):
for j in range(10):
- Counter2.new(n1=i, n2=j)
+ Counter2(n1=i, n2=j)
def counterEqual(self, counters, value):
self.assertEquals([(c.n1, c.n2) for c in counters], value)
@@ -517,10 +517,10 @@
def inserts(self):
for n in ['jane', 'tim', 'bob', 'jake']:
- Person.new(name=n)
+ Person(name=n)
for p in ['555-555-5555', '555-394-2930',
'444-382-4854']:
- Phone.new(phone=p)
+ Phone(phone=p)
def testDefaultOrder(self):
self.assertEqual(list(Person.select('all')),
@@ -531,7 +531,7 @@
return
nickname = StringCol('nickname', length=10)
Person.addColumn(nickname, changeSchema=True)
- n = Person.new(name='robert', nickname='bob')
+ n = Person(name='robert', nickname='bob')
self.assertEqual([p.name for p in Person.select('all')],
['bob', 'jake', 'jane', 'robert', 'tim'])
Person.delColumn(nickname, changeSchema=True)
@@ -613,20 +613,19 @@
def testClassCreate(self):
if not self.supportAuto:
return
- import sys
class AutoTest(SQLObject):
_fromDatabase = True
_connection = connection()
- john = AutoTest.new(firstName='john',
- lastName='doe',
- age=10,
- created=DateTime.now(),
- wannahavefun=False)
- jane = AutoTest.new(firstName='jane',
- lastName='doe',
- happy='N',
- created=DateTime.now(),
- wannahavefun=True)
+ john = AutoTest(firstName='john',
+ lastName='doe',
+ age=10,
+ created=DateTime.now(),
+ wannahavefun=False)
+ jane = AutoTest(firstName='jane',
+ lastName='doe',
+ happy='N',
+ created=DateTime.now(),
+ wannahavefun=True)
self.failIf(john.wannahavefun)
self.failUnless(jane.wannahavefun)
del classregistry.registry(AutoTest._registry).classes['AutoTest']
@@ -651,9 +650,9 @@
def inserts(self):
for n in ['bob', 'tim', 'jane', 'joe', 'fred', 'barb']:
- PersonJoiner.new(name=n)
+ PersonJoiner(name=n)
for z in ['11111', '22222', '33333', '44444']:
- AddressJoiner.new(zip=z)
+ AddressJoiner(zip=z)
def testJoin(self):
b = PersonJoiner.byName('bob')
@@ -693,12 +692,12 @@
classes = [PersonJoiner2, AddressJoiner2]
def inserts(self):
- p1 = PersonJoiner2.new(name='bob')
- p2 = PersonJoiner2.new(name='sally')
+ p1 = PersonJoiner2(name='bob')
+ p2 = PersonJoiner2(name='sally')
for z in ['11111', '22222', '33333']:
- a = AddressJoiner2.new(zip=z, personJoiner2=p1)
+ a = AddressJoiner2(zip=z, personJoiner2=p1)
#p1.addAddressJoiner2(a)
- AddressJoiner2.new(zip='00000', personJoiner2=p2)
+ AddressJoiner2(zip='00000', personJoiner2=p2)
def test(self):
bob = PersonJoiner2.byName('bob')
@@ -711,7 +710,7 @@
z.zip = 'xxxxx'
id = z.id
del z
- z = AddressJoiner2(id)
+ z = AddressJoiner2.get(id)
self.assertEqual(z.zip, 'xxxxx')
def testDefaultOrder(self):
@@ -737,15 +736,15 @@
classes = [Super, Sub]
def testSuper(self):
- s1 = Super.new(name='one')
- s2 = Super.new(name='two')
- s3 = Super(s1.id)
+ s1 = Super(name='one')
+ s2 = Super(name='two')
+ s3 = Super.get(s1.id)
self.assertEqual(s1, s3)
def testSub(self):
- s1 = Sub.new(name='one', name2='1')
- s2 = Sub.new(name='two', name2='2')
- s3 = Sub(s1.id)
+ s1 = Sub(name='one', name2='1')
+ s2 = Sub(name='two', name2='2')
+ s3 = Sub.get(s1.id)
self.assertEqual(s1, s3)
@@ -761,8 +760,8 @@
classes = [SyncTest]
def inserts(self):
- SyncTest.new(name='bob')
- SyncTest.new(name='tim')
+ SyncTest(name='bob')
+ SyncTest(name='tim')
def testExpire(self):
conn = SyncTest._connection
@@ -792,19 +791,19 @@
classes = [SOValidation]
def testValidate(self):
- t = SOValidation.new(name='hey')
+ t = SOValidation(name='hey')
self.assertRaises(validators.InvalidField, setattr, t,
'name', '!!!')
t.name = 'you'
def testConfirmType(self):
- t = SOValidation.new(name2='hey')
+ t = SOValidation(name2='hey')
self.assertRaises(validators.InvalidField, setattr, t,
'name2', 1)
t.name2 = 'you'
def testWrapType(self):
- t = SOValidation.new(name3=1)
+ t = SOValidation(name3=1)
self.assertRaises(validators.InvalidField, setattr, t,
'name3', 'x')
t.name3 = 1L
@@ -863,11 +862,11 @@
classes = [SOStringID]
def testStringID(self):
- t = SOStringID.new(id='hey', val='whatever')
+ t = SOStringID(id='hey', val='whatever')
t2 = SOStringID.byVal('whatever')
self.assertEqual(t, t2)
- t3 = SOStringID.new(id='you', val='nowhere')
- t4 = SOStringID('you')
+ t3 = SOStringID(id='you', val='nowhere')
+ t4 = SOStringID.get('you')
self.assertEqual(t3, t4)
@@ -894,8 +893,8 @@
def test(self):
- st1 = SOStyleTest1.new(a='something', st2=None)
- st2 = SOStyleTest2.new(b='whatever')
+ st1 = SOStyleTest1(a='something', st2=None)
+ st2 = SOStyleTest2(b='whatever')
st1.st2 = st2
self.assertEqual(st1._SO_columnDict['st2ID'].dbName, 'idst2')
self.assertEqual(st1.st2, st2)
|
|
From: <bbo...@us...> - 2004-01-21 22:24:08
|
Update of /cvsroot/sqlobject/SQLObject/docs In directory sc8-pr-cvs1:/tmp/cvs-serv1886/docs Modified Files: Authors.txt Log Message: Added myself to the list of contributors. Index: Authors.txt =================================================================== RCS file: /cvsroot/sqlobject/SQLObject/docs/Authors.txt,v retrieving revision 1.4 retrieving revision 1.5 diff -C2 -d -r1.4 -r1.5 *** Authors.txt 1 Nov 2003 20:33:18 -0000 1.4 --- Authors.txt 21 Jan 2004 22:24:05 -0000 1.5 *************** *** 13,14 **** --- 13,15 ---- * James Ralston <jralston at hotmail.com> * Sidnei da Silva <sidnei at awkly.org> + * Brad Bollenbach <brad at bbnet.ca> |
|
From: <bbo...@us...> - 2004-01-21 22:21:36
|
Update of /cvsroot/sqlobject/SQLObject/tests
In directory sc8-pr-cvs1:/tmp/cvs-serv32608/tests
Modified Files:
test.py
Log Message:
Fixed so that class BOOL is a new-style class (inherits from object), so that
the call to registerConverter actually means something (without inheriting from
object, the type is just <type 'instance'>, which is useless when registering a
converter for that type.)
There hadn't been any tests for this. :/
Index: test.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/tests/test.py,v
retrieving revision 1.37
retrieving revision 1.38
diff -C2 -d -r1.37 -r1.38
*** test.py 4 Dec 2003 16:46:31 -0000 1.37
--- test.py 21 Jan 2004 22:21:33 -0000 1.38
***************
*** 89,92 ****
--- 89,102 ----
me = StringCol(length=10)
+ class Student(SQLObject):
+ is_smart = BoolCol()
+
+ class BoolColTest(SQLObjectTest):
+ classes = [Student]
+
+ def testBoolCol(self):
+ student = Student.new(is_smart = False)
+ self.assertEqual(student.is_smart, False)
+
class TestCase34(SQLObjectTest):
|
|
From: <bbo...@us...> - 2004-01-21 22:21:35
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv32608/SQLObject
Modified Files:
Converters.py
Log Message:
Fixed so that class BOOL is a new-style class (inherits from object), so that
the call to registerConverter actually means something (without inheriting from
object, the type is just <type 'instance'>, which is useless when registering a
converter for that type.)
There hadn't been any tests for this. :/
Index: Converters.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/Converters.py,v
retrieving revision 1.11
retrieving revision 1.12
diff -C2 -d -r1.11 -r1.12
*** Converters.py 14 Jan 2004 14:34:28 -0000 1.11
--- Converters.py 21 Jan 2004 22:21:32 -0000 1.12
***************
*** 24,28 ****
if type(1==1) == type(1):
! class BOOL:
def __init__(self, value):
self.value = not not value
--- 24,28 ----
if type(1==1) == type(1):
! class BOOL(object):
def __init__(self, value):
self.value = not not value
|
|
From: <dre...@us...> - 2004-01-14 14:34:31
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv9410
Modified Files:
Col.py Converters.py DBConnection.py SQLBuilder.py
Log Message:
More sybase support. Table creation, toggle IDENTITY_INSERT if id is passed. Added NumericType converter. Limit is not supported.
Index: Col.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/Col.py,v
retrieving revision 1.37
retrieving revision 1.38
diff -C2 -d -r1.37 -r1.38
*** Col.py 13 Jan 2004 15:34:37 -0000 1.37
--- Col.py 14 Jan 2004 14:34:28 -0000 1.38
***************
*** 171,175 ****
def _sqlType(self):
if self.customSQLType is None:
! raise ValueError, "Col cannot be used for automatic schema creation (too abstract)"
else:
return self.customSQLType
--- 171,177 ----
def _sqlType(self):
if self.customSQLType is None:
! raise ValueError, ("Col %s (%s) cannot be used for automatic "
! "schema creation (too abstract)" %
! (self.name, self.__class__))
else:
return self.customSQLType
***************
*** 340,343 ****
--- 342,348 ----
return 'INT'
+ def _sybaseType(self):
+ return 'INT'
+
def _firebirdType(self):
return 'INT'
***************
*** 378,381 ****
--- 383,398 ----
return sql
+ def sybaseCreateSQL(self):
+ from SQLObject import findClass
+ sql = SOKeyCol.sybaseCreateSQL(self)
+ other = findClass(self.foreignKey)
+ tName = other._table
+ idName = other._idName
+ reference = ('REFERENCES %(tName)s(%(idName)s) ' %
+ {'tName':tName,
+ 'idName':idName})
+ sql = ' '.join([sql, reference])
+ return sql
+
class ForeignKey(KeyCol):
Index: Converters.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/Converters.py,v
retrieving revision 1.10
retrieving revision 1.11
diff -C2 -d -r1.10 -r1.11
*** Converters.py 13 Jan 2004 15:34:37 -0000 1.10
--- Converters.py 14 Jan 2004 14:34:28 -0000 1.11
***************
*** 17,20 ****
--- 17,26 ----
datetime = None
+ try:
+ import Sybase
+ NumericType=Sybase.NumericType
+ except ImportError:
+ NumericType = None
+
if type(1==1) == type(1):
class BOOL:
***************
*** 102,105 ****
--- 108,114 ----
registerConverter(type(0L), IntConverter)
+ if NumericType:
+ registerConverter(NumericType, IntConverter)
+
def BoolConverter(value, db):
if db in ('postgres',):
***************
*** 171,172 ****
--- 180,183 ----
else:
return reprFunc(db)
+
+
Index: DBConnection.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/DBConnection.py,v
retrieving revision 1.61
retrieving revision 1.62
diff -C2 -d -r1.61 -r1.62
*** DBConnection.py 13 Jan 2004 15:37:58 -0000 1.61
--- DBConnection.py 14 Jan 2004 14:34:28 -0000 1.62
***************
*** 807,810 ****
--- 807,813 ----
names = [idName] + names
values = [id] + values
+ c.execute('SET IDENTITY_INSERT %s ON' % table)
+ else:
+ c.execute('SET IDENTITY_INSERT %s OFF' % table)
q = self._insertSQL(table, names, values)
if self.debug:
***************
*** 812,816 ****
c.execute(q)
if id is None:
! id = self.insert_id()
if self.debugOutput:
self.printDebug(conn, id, 'QueryIns', 'result')
--- 815,819 ----
c.execute(q)
if id is None:
! id = self.insert_id(conn)
if self.debugOutput:
self.printDebug(conn, id, 'QueryIns', 'result')
***************
*** 818,826 ****
def _queryAddLimitOffset(self, query, start, end):
! if not start:
! return "%s LIMIT %i" % (query, end)
! if not end:
! return "%s LIMIT %i, -1" % (query, start)
! return "%s LIMIT %i, %i" % (query, start, end-start)
def createColumn(self, soClass, col):
--- 821,826 ----
def _queryAddLimitOffset(self, query, start, end):
! # XXX Sybase doesn't support LIMIT
! return query
def createColumn(self, soClass, col):
***************
*** 828,836 ****
def createIDColumn(self, soClass):
- #return '%s INT PRIMARY KEY AUTO_INCREMENT' % soClass._idName
return '%s NUMERIC(18,0) IDENTITY' % soClass._idName
def joinSQLType(self, join):
! return 'NUMERIC(18,0) NOT NULL' #INT NOT NULL'
SHOW_TABLES="SELECT name FROM sysobjects WHERE type='U'"
--- 828,835 ----
def createIDColumn(self, soClass):
return '%s NUMERIC(18,0) IDENTITY' % soClass._idName
def joinSQLType(self, join):
! return 'NUMERIC(18,0) NOT NULL'
SHOW_TABLES="SELECT name FROM sysobjects WHERE type='U'"
Index: SQLBuilder.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/SQLBuilder.py,v
retrieving revision 1.15
retrieving revision 1.16
diff -C2 -d -r1.15 -r1.16
*** SQLBuilder.py 4 Dec 2003 16:46:31 -0000 1.15
--- SQLBuilder.py 14 Jan 2004 14:34:28 -0000 1.16
***************
*** 591,596 ****
for expr in tests.split('\n'):
if not expr.strip(): continue
- print expr
if expr.startswith('>>> '):
expr = expr[4:]
- print repr(eval(expr))
--- 591,594 ----
|
|
From: <dre...@us...> - 2004-01-13 15:38:01
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv3563
Modified Files:
DBConnection.py
Log Message:
Remove leftover from debugging
Index: DBConnection.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/DBConnection.py,v
retrieving revision 1.60
retrieving revision 1.61
diff -C2 -d -r1.60 -r1.61
*** DBConnection.py 13 Jan 2004 15:34:37 -0000 1.60
--- DBConnection.py 13 Jan 2004 15:37:58 -0000 1.61
***************
*** 270,274 ****
def createTable(self, soClass):
- import pdb; pdb.set_trace()
self.query('CREATE TABLE %s (\n%s\n)' % \
(soClass._table, self.createColumns(soClass)))
--- 270,273 ----
|
|
From: <dre...@us...> - 2004-01-13 15:34:45
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv2613
Modified Files:
Col.py Converters.py DBConnection.py
Log Message:
A few more improvements on sybase support
Index: Col.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/Col.py,v
retrieving revision 1.36
retrieving revision 1.37
diff -C2 -d -r1.36 -r1.37
*** Col.py 13 Jan 2004 15:24:45 -0000 1.36
--- Col.py 13 Jan 2004 15:34:37 -0000 1.37
***************
*** 429,433 ****
def _sybaseType(self):
! return self._postgresType()
class DateTimeCol(Col):
--- 429,433 ----
def _sybaseType(self):
! return 'DATETIME'
class DateTimeCol(Col):
Index: Converters.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/Converters.py,v
retrieving revision 1.9
retrieving revision 1.10
diff -C2 -d -r1.9 -r1.10
*** Converters.py 4 Dec 2003 16:46:31 -0000 1.9
--- Converters.py 13 Jan 2004 15:34:37 -0000 1.10
***************
*** 84,88 ****
def StringLikeConverter(value, db):
! if db in ('mysql', 'postgres'):
for orig, repl in sqlStringReplace:
value = value.replace(orig, repl)
--- 84,88 ----
def StringLikeConverter(value, db):
! if db in ('mysql', 'postgres', 'sybase'):
for orig, repl in sqlStringReplace:
value = value.replace(orig, repl)
Index: DBConnection.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/DBConnection.py,v
retrieving revision 1.59
retrieving revision 1.60
diff -C2 -d -r1.59 -r1.60
*** DBConnection.py 13 Jan 2004 15:24:45 -0000 1.59
--- DBConnection.py 13 Jan 2004 15:34:37 -0000 1.60
***************
*** 270,273 ****
--- 270,274 ----
def createTable(self, soClass):
+ import pdb; pdb.set_trace()
self.query('CREATE TABLE %s (\n%s\n)' % \
(soClass._table, self.createColumns(soClass)))
***************
*** 768,771 ****
--- 769,775 ----
class SybaseConnection(DBAPI):
+ supportTransactions = True
+ dbName = 'sybase'
+
def __init__(self, db, user, passwd='', host='localhost',
autoCommit=0, **kw):
|
|
From: <dre...@us...> - 2004-01-13 15:24:48
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv592
Modified Files:
Col.py DBConnection.py
Log Message:
Fix BoolCol for Sybase and queryInsertID, which was lacking one parameter
Index: Col.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/Col.py,v
retrieving revision 1.35
retrieving revision 1.36
diff -C2 -d -r1.35 -r1.36
*** Col.py 4 Dec 2003 16:46:31 -0000 1.35
--- Col.py 13 Jan 2004 15:24:45 -0000 1.36
***************
*** 307,310 ****
--- 307,313 ----
return "TINYINT"
+ def _sybaseType(self):
+ return "BIT"
+
class BoolCol(Col):
baseClass = SOBoolCol
Index: DBConnection.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/DBConnection.py,v
retrieving revision 1.58
retrieving revision 1.59
diff -C2 -d -r1.58 -r1.59
*** DBConnection.py 16 Dec 2003 05:41:11 -0000 1.58
--- DBConnection.py 13 Jan 2004 15:24:45 -0000 1.59
***************
*** 799,809 ****
database=self.db, auto_commit=self.autoCommit)
! def _queryInsertID(self, conn, table, idName, names, values):
c = conn.cursor()
q = self._insertSQL(table, names, values)
if self.debug:
print 'QueryIns: %s' % q
c.execute(q)
! return self.insert_id(conn)
def _queryAddLimitOffset(self, query, start, end):
--- 799,816 ----
database=self.db, auto_commit=self.autoCommit)
! def _queryInsertID(self, conn, table, idName, id, names, values):
c = conn.cursor()
+ if id is not None:
+ names = [idName] + names
+ values = [id] + values
q = self._insertSQL(table, names, values)
if self.debug:
print 'QueryIns: %s' % q
c.execute(q)
! if id is None:
! id = self.insert_id()
! if self.debugOutput:
! self.printDebug(conn, id, 'QueryIns', 'result')
! return id
def _queryAddLimitOffset(self, query, start, end):
|
|
From: <ian...@us...> - 2003-12-16 05:42:22
|
Update of /cvsroot/sqlobject/SQLObject/docs In directory sc8-pr-cvs1:/tmp/cvs-serv18487/docs Modified Files: News.txt Log Message: Added news Index: News.txt =================================================================== RCS file: /cvsroot/sqlobject/SQLObject/docs/News.txt,v retrieving revision 1.12 retrieving revision 1.13 diff -C2 -d -r1.12 -r1.13 *** News.txt 12 Nov 2003 17:08:29 -0000 1.12 --- News.txt 16 Dec 2003 05:42:19 -0000 1.13 *************** *** 8,11 **** --- 8,20 ---- .. _start: + SQLObject 0.5.2 + =============== + + Bugs + ---- + + * Fixed bug which did not release connections after database (query) + error. + SQLObject 0.5.1 =============== |
|
From: <ian...@us...> - 2003-12-16 05:41:15
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv18229/SQLObject
Modified Files:
DBConnection.py
Log Message:
Release connection after database error
(patch from Ken Kinder <ke...@ke...>)
Index: DBConnection.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/DBConnection.py,v
retrieving revision 1.57
retrieving revision 1.58
diff -C2 -d -r1.57 -r1.58
*** DBConnection.py 4 Dec 2003 16:44:04 -0000 1.57
--- DBConnection.py 16 Dec 2003 05:41:11 -0000 1.58
***************
*** 71,76 ****
def _runWithConnection(self, meth, *args):
conn = self.getConnection()
! val = meth(conn, *args)
! self.releaseConnection(conn)
return val
--- 71,78 ----
def _runWithConnection(self, meth, *args):
conn = self.getConnection()
! try:
! val = meth(conn, *args)
! finally:
! self.releaseConnection(conn)
return val
***************
*** 905,914 ****
except kinterbasdb.ProgrammingError:
pass
- val = meth(conn, *args)
try:
! conn.commit()
! except kinterbasdb.ProgrammingError:
! pass
! self.releaseConnection(conn)
return val
--- 907,918 ----
except kinterbasdb.ProgrammingError:
pass
try:
! val = meth(conn, *args)
! try:
! conn.commit()
! except kinterbasdb.ProgrammingError:
! pass
! finally:
! self.releaseConnection(conn)
return val
|
|
From: <dre...@us...> - 2003-12-04 16:46:34
|
Update of /cvsroot/sqlobject/SQLObject/examples
In directory sc8-pr-cvs1:/tmp/cvs-serv17015/examples
Modified Files:
codebits.py config.py people.py personaddress.py setup.py
simpleperson.py userrole.py
Log Message:
And while we are at it, run reindent.py
Index: codebits.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/examples/codebits.py,v
retrieving revision 1.4
retrieving revision 1.5
diff -C2 -d -r1.4 -r1.5
*** codebits.py 30 Nov 2003 21:46:11 -0000 1.4
--- codebits.py 4 Dec 2003 16:46:31 -0000 1.5
***************
*** 122,126 ****
firstName = StringCol()
lastName = StringCol()
!
def _get_employee(self):
value = Employee.selectBy(person=self)
--- 122,126 ----
firstName = StringCol()
lastName = StringCol()
!
def _get_employee(self):
value = Employee.selectBy(person=self)
***************
*** 194,198 ****
city = StringCol()
state = StringCol(length=2)
!
latitude = FloatCol()
longitude = FloatCol()
--- 194,198 ----
city = StringCol()
state = StringCol(length=2)
!
latitude = FloatCol()
longitude = FloatCol()
***************
*** 201,205 ****
SQLObject._init(self, id)
self._coords = SOCoords(self)
!
def _get_coords(self):
return self._coords
--- 201,205 ----
SQLObject._init(self, id)
self._coords = SOCoords(self)
!
def _get_coords(self):
return self._coords
***************
*** 256,258 ****
print "Showing page %i of %i" % (start/size + 1, total/size + 1)
## end snippet
-
--- 256,257 ----
Index: config.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/examples/config.py,v
retrieving revision 1.1
retrieving revision 1.2
diff -C2 -d -r1.1 -r1.2
*** config.py 28 Jun 2003 22:17:36 -0000 1.1
--- config.py 4 Dec 2003 16:46:31 -0000 1.2
***************
*** 14,16 ****
conn = DBMConnection('database/')
conn = MySQLConnection(user='test', db='test')
-
--- 14,15 ----
Index: people.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/examples/people.py,v
retrieving revision 1.5
retrieving revision 1.6
diff -C2 -d -r1.5 -r1.6
*** people.py 14 Mar 2003 03:52:15 -0000 1.5
--- people.py 4 Dec 2003 16:46:31 -0000 1.6
***************
*** 37,41 ****
notNull=1),
StringCol('firstName', length=30,
! notNull=1),
StringCol('middleInitial', length=1, default=None),
StringCol('lastName', length=50, notNull=1)]
--- 37,41 ----
notNull=1),
StringCol('firstName', length=30,
! notNull=1),
StringCol('middleInitial', length=1, default=None),
StringCol('lastName', length=50, notNull=1)]
***************
*** 52,56 ****
f.close()
return v
!
def _set_image(self, value):
# assume we get a string for the image
--- 52,56 ----
f.close()
return v
!
def _set_image(self, value):
# assume we get a string for the image
***************
*** 58,62 ****
f.write(value)
f.close()
!
def _del_image(self, value):
# I usually wouldn't include a method like this, but for
--- 58,62 ----
f.write(value)
f.close()
!
def _del_image(self, value):
# I usually wouldn't include a method like this, but for
Index: personaddress.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/examples/personaddress.py,v
retrieving revision 1.1
retrieving revision 1.2
diff -C2 -d -r1.1 -r1.2
*** personaddress.py 28 Jun 2003 22:17:36 -0000 1.1
--- personaddress.py 4 Dec 2003 16:46:31 -0000 1.2
***************
*** 22,26 ****
zip = StringCol(length=9)
person = ForeignKey('Person')
! ## end snippet
def reset():
--- 22,26 ----
zip = StringCol(length=9)
person = ForeignKey('Person')
! ## end snippet
def reset():
***************
*** 29,33 ****
Address.dropTable(ifExists=True)
Address.createTable()
!
reset()
--- 29,33 ----
Address.dropTable(ifExists=True)
Address.createTable()
!
reset()
Index: setup.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/examples/setup.py,v
retrieving revision 1.1
retrieving revision 1.2
diff -C2 -d -r1.1 -r1.2
*** setup.py 28 Jun 2003 22:17:36 -0000 1.1
--- setup.py 4 Dec 2003 16:46:31 -0000 1.2
***************
*** 17,20 ****
value.dropTable(ifExists=True)
value.createTable()
-
-
--- 17,18 ----
Index: simpleperson.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/examples/simpleperson.py,v
retrieving revision 1.1
retrieving revision 1.2
diff -C2 -d -r1.1 -r1.2
*** simpleperson.py 28 Jun 2003 22:17:36 -0000 1.1
--- simpleperson.py 4 Dec 2003 16:46:31 -0000 1.2
***************
*** 38,42 ****
Person.dropTable(ifExists=True)
Person.createTable()
!
## Get rid of any tables we have left over...
--- 38,42 ----
Person.dropTable(ifExists=True)
Person.createTable()
!
## Get rid of any tables we have left over...
***************
*** 95,99 ****
#-- Again, no database access, since we're just grabbing the same
#-- instance we already had.
! print p2
#>> <Person 1 firstName='John' middleInitial='Q' lastName='Doe'>
print p is p2
--- 95,99 ----
#-- Again, no database access, since we're just grabbing the same
#-- instance we already had.
! print p2
#>> <Person 1 firstName='John' middleInitial='Q' lastName='Doe'>
print p is p2
Index: userrole.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/examples/userrole.py,v
retrieving revision 1.1
retrieving revision 1.2
diff -C2 -d -r1.1 -r1.2
*** userrole.py 28 Jun 2003 22:17:36 -0000 1.1
--- userrole.py 4 Dec 2003 16:46:31 -0000 1.2
***************
*** 31,35 ****
# Role.dropTable(ifExists=True)
# Role.createTable()
!
setup.reset()
--- 31,35 ----
# Role.dropTable(ifExists=True)
# Role.createTable()
!
setup.reset()
|
|
From: <dre...@us...> - 2003-12-04 16:46:34
|
Update of /cvsroot/sqlobject/SQLObject/tests
In directory sc8-pr-cvs1:/tmp/cvs-serv17015/tests
Modified Files:
SQLObjectTest.py test.py
Log Message:
And while we are at it, run reindent.py
Index: SQLObjectTest.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/tests/SQLObjectTest.py,v
retrieving revision 1.20
retrieving revision 1.21
diff -C2 -d -r1.20 -r1.21
*** SQLObjectTest.py 4 Dec 2003 16:44:05 -0000 1.20
--- SQLObjectTest.py 4 Dec 2003 16:46:31 -0000 1.21
***************
*** 159,161 ****
__all__ = ['SQLObjectTest', 'setDatabaseType', 'connection',
'supportedDatabases']
-
--- 159,160 ----
Index: test.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/tests/test.py,v
retrieving revision 1.36
retrieving revision 1.37
diff -C2 -d -r1.36 -r1.37
*** test.py 4 Dec 2003 16:44:05 -0000 1.36
--- test.py 4 Dec 2003 16:46:31 -0000 1.37
***************
*** 582,586 ****
age INT DEFAULT 0,
created VARCHAT(40) NOT NULL,
! happy char(1) DEFAULT 'Y' NOT NULL
)
"""
--- 582,586 ----
age INT DEFAULT 0,
created VARCHAT(40) NOT NULL,
! happy char(1) DEFAULT 'Y' NOT NULL
)
"""
***************
*** 1008,1010 ****
coverage.stop()
coverModules()
-
--- 1008,1009 ----
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv17015/SQLObject
Modified Files:
Cache.py Col.py Constraints.py Converters.py Join.py
SQLBuilder.py SQLObject.py Style.py
Log Message:
And while we are at it, run reindent.py
Index: Cache.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/Cache.py,v
retrieving revision 1.10
retrieving revision 1.11
diff -C2 -d -r1.10 -r1.11
*** Cache.py 26 Sep 2003 20:05:14 -0000 1.10
--- Cache.py 4 Dec 2003 16:46:31 -0000 1.11
***************
*** 10,14 ****
CacheFactory caches object creation. Each object should be
referenced by a single hashable ID (note tuples of hashable
! values are also hashable).
"""
--- 10,14 ----
CacheFactory caches object creation. Each object should be
referenced by a single hashable ID (note tuples of hashable
! values are also hashable).
"""
***************
*** 34,38 ****
it will be returned.
"""
!
self.cullFrequency = cullFrequency
self.cullCount = cullFrequency
--- 34,38 ----
it will be returned.
"""
!
self.cullFrequency = cullFrequency
self.cullCount = cullFrequency
Index: Col.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/Col.py,v
retrieving revision 1.34
retrieving revision 1.35
diff -C2 -d -r1.34 -r1.35
*** Col.py 4 Dec 2003 16:44:04 -0000 1.34
--- Col.py 4 Dec 2003 16:46:31 -0000 1.35
***************
*** 90,94 ****
# if they don't give us a specific database name for
# the column, we separate the mixedCase into mixed_case
! # and assume that.
if dbName is None:
self.dbName = soClass._style.pythonAttrToDBColumn(self.name)
--- 90,94 ----
# if they don't give us a specific database name for
# the column, we separate the mixedCase into mixed_case
! # and assume that.
if dbName is None:
self.dbName = soClass._style.pythonAttrToDBColumn(self.name)
***************
*** 210,214 ****
# Ian Sparks pointed out that fb is picky about the order
# of the NOT NULL clause in a create statement. So, we handle
! # them differently for Enum columns.
if not isinstance(self, SOEnumCol):
return ' '.join([self.dbName, self._firebirdType()] + self._extraSQL())
--- 210,214 ----
# Ian Sparks pointed out that fb is picky about the order
# of the NOT NULL clause in a create statement. So, we handle
! # them differently for Enum columns.
if not isinstance(self, SOEnumCol):
return ' '.join([self.dbName, self._firebirdType()] + self._extraSQL())
***************
*** 491,493 ****
all.append(key)
__all__ = all
-
--- 491,492 ----
Index: Constraints.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/Constraints.py,v
retrieving revision 1.3
retrieving revision 1.4
diff -C2 -d -r1.3 -r1.4
*** Constraints.py 25 Sep 2003 20:40:25 -0000 1.3
--- Constraints.py 4 Dec 2003 16:46:31 -0000 1.4
***************
*** 59,62 ****
% self.length,
obj, col, value)
-
-
--- 59,60 ----
Index: Converters.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/Converters.py,v
retrieving revision 1.8
retrieving revision 1.9
diff -C2 -d -r1.8 -r1.9
*** Converters.py 22 Oct 2003 18:11:56 -0000 1.8
--- Converters.py 4 Dec 2003 16:46:31 -0000 1.9
***************
*** 171,173 ****
else:
return reprFunc(db)
-
--- 171,172 ----
Index: Join.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/Join.py,v
retrieving revision 1.6
retrieving revision 1.7
diff -C2 -d -r1.6 -r1.7
*** Join.py 4 Nov 2003 02:28:53 -0000 1.6
--- Join.py 4 Dec 2003 16:46:31 -0000 1.7
***************
*** 132,136 ****
if not self.otherColumn:
self.otherColumn = self.soClass._style.tableReference(otherClass._table)
!
def hasIntermediateTable(self):
--- 132,136 ----
if not self.otherColumn:
self.otherColumn = self.soClass._style.tableReference(otherClass._table)
!
def hasIntermediateTable(self):
***************
*** 170,172 ****
def capitalize(name):
return name[0].capitalize() + name[1:]
-
--- 170,171 ----
Index: SQLBuilder.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/SQLBuilder.py,v
retrieving revision 1.14
retrieving revision 1.15
diff -C2 -d -r1.14 -r1.15
*** SQLBuilder.py 4 Nov 2003 02:25:33 -0000 1.14
--- SQLBuilder.py 4 Dec 2003 16:46:31 -0000 1.15
***************
*** 484,488 ****
registerConverter(Update, SQLExprConverter)
!
class Delete(SQLExpression):
"""To be safe, this will signal an error if there is no where clause,
--- 484,488 ----
registerConverter(Update, SQLExprConverter)
!
class Delete(SQLExpression):
"""To be safe, this will signal an error if there is no where clause,
***************
*** 595,597 ****
expr = expr[4:]
print repr(eval(expr))
-
--- 595,596 ----
Index: SQLObject.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/SQLObject.py,v
retrieving revision 1.65
retrieving revision 1.66
diff -C2 -d -r1.65 -r1.66
*** SQLObject.py 4 Dec 2003 16:44:04 -0000 1.65
--- SQLObject.py 4 Dec 2003 16:46:31 -0000 1.66
***************
*** 1182,1186 ****
return list(self)[:value.stop]
!
if value.start:
assert value.start >= 0
--- 1182,1186 ----
return list(self)[:value.stop]
!
if value.start:
assert value.start >= 0
Index: Style.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/Style.py,v
retrieving revision 1.5
retrieving revision 1.6
diff -C2 -d -r1.5 -r1.6
*** Style.py 15 Jul 2003 02:16:07 -0000 1.5
--- Style.py 4 Dec 2003 16:46:31 -0000 1.6
***************
*** 151,153 ****
return _underToMixedRE.sub(lambda m: m.group(0)[1].upper(),
name)
-
--- 151,152 ----
|
|
From: <dre...@us...> - 2003-12-04 16:46:34
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject/include
In directory sc8-pr-cvs1:/tmp/cvs-serv17015/SQLObject/include
Modified Files:
Validator.py
Log Message:
And while we are at it, run reindent.py
Index: Validator.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/include/Validator.py,v
retrieving revision 1.3
retrieving revision 1.4
diff -C2 -d -r1.3 -r1.4
*** Validator.py 26 Sep 2003 07:12:59 -0000 1.3
--- Validator.py 4 Dec 2003 16:46:31 -0000 1.4
***************
*** 1044,1046 ****
('1800', 15)],
}
-
--- 1044,1045 ----
|
|
From: <dre...@us...> - 2003-12-04 16:44:08
|
Update of /cvsroot/sqlobject/SQLObject/tests
In directory sc8-pr-cvs1:/tmp/cvs-serv16262/tests
Modified Files:
SQLObjectTest.py test.py
Log Message:
Merge cascadegeddon-branch. This implements foreign key constraints (postgresql-only) and 'DELETE CASCADE'/'DELETE RESTRICT' (should work on all db's as it's done manually). We don't support 'UPDATE CASCADE'/'UPDATE RESTRICT' yet. To get it working, just pass the 'cascade' keyword argument to ForeignKey. True=CASCADE, False=RESTRICT, None(default)=The old behavior.
Index: SQLObjectTest.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/tests/SQLObjectTest.py,v
retrieving revision 1.19
retrieving revision 1.20
diff -C2 -d -r1.19 -r1.20
*** SQLObjectTest.py 4 Dec 2003 16:36:17 -0000 1.19
--- SQLObjectTest.py 4 Dec 2003 16:44:05 -0000 1.20
***************
*** 118,122 ****
__connection__.query(c.drop)
elif hasattr(c, 'dropTable'):
! c.dropTable(ifExists=True)
if hasattr(c, '%sCreate' % self.databaseName):
--- 118,122 ----
__connection__.query(c.drop)
elif hasattr(c, 'dropTable'):
! c.dropTable(ifExists=True, cascade=True)
if hasattr(c, '%sCreate' % self.databaseName):
Index: test.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/tests/test.py,v
retrieving revision 1.35
retrieving revision 1.36
diff -C2 -d -r1.35 -r1.36
*** test.py 4 Dec 2003 16:36:17 -0000 1.35
--- test.py 4 Dec 2003 16:44:05 -0000 1.36
***************
*** 120,123 ****
--- 120,241 ----
self.assertEqual(tcc2.other, tc4a)
+ class TestSO5(SQLObject):
+ name = StringCol(length=10, dbName='name_col')
+ other = ForeignKey('TestSO6', default=None, cascade=True)
+ another = ForeignKey('TestSO7', default=None, cascade=True)
+
+ class TestSO6(SQLObject):
+ name = StringCol(length=10, dbName='name_col')
+ other = ForeignKey('TestSO7', default=None, cascade=True)
+
+ class TestSO7(SQLObject):
+ name = StringCol(length=10, dbName='name_col')
+
+ class TestCase567(SQLObjectTest):
+
+ classes = [TestSO7, TestSO6, TestSO5]
+
+ def testForeignKeyDestroySelfCascade(self):
+ tc5 = TestSO5.new(name='a')
+ tc6a = TestSO6.new(name='1')
+ tc5.other = tc6a
+ tc7a = TestSO7.new(name='2')
+ tc6a.other = tc7a
+ tc5.another = tc7a
+ self.assertEqual(tc5.other, tc6a)
+ self.assertEqual(tc5.otherID, tc6a.id)
+ self.assertEqual(tc6a.other, tc7a)
+ self.assertEqual(tc6a.otherID, tc7a.id)
+ self.assertEqual(tc5.other.other, tc7a)
+ self.assertEqual(tc5.other.otherID, tc7a.id)
+ self.assertEqual(tc5.another, tc7a)
+ self.assertEqual(tc5.anotherID, tc7a.id)
+ self.assertEqual(tc5.other.other, tc5.another)
+ self.assertEqual(TestSO5.select().count(), 1)
+ self.assertEqual(TestSO6.select().count(), 1)
+ self.assertEqual(TestSO7.select().count(), 1)
+ tc6b = TestSO6.new(name='3')
+ tc6c = TestSO6.new(name='4')
+ tc7b = TestSO7.new(name='5')
+ tc6b.other = tc7b
+ tc6c.other = tc7b
+ self.assertEqual(TestSO5.select().count(), 1)
+ self.assertEqual(TestSO6.select().count(), 3)
+ self.assertEqual(TestSO7.select().count(), 2)
+ tc6b.destroySelf()
+ self.assertEqual(TestSO5.select().count(), 1)
+ self.assertEqual(TestSO6.select().count(), 2)
+ self.assertEqual(TestSO7.select().count(), 2)
+ tc7b.destroySelf()
+ self.assertEqual(TestSO5.select().count(), 1)
+ self.assertEqual(TestSO6.select().count(), 1)
+ self.assertEqual(TestSO7.select().count(), 1)
+ tc7a.destroySelf()
+ self.assertEqual(TestSO5.select().count(), 0)
+ self.assertEqual(TestSO6.select().count(), 0)
+ self.assertEqual(TestSO7.select().count(), 0)
+
+ def testForeignKeyDropTableCascade(self):
+ tc5a = TestSO5.new(name='a')
+ tc6a = TestSO6.new(name='1')
+ tc5a.other = tc6a
+ tc7a = TestSO7.new(name='2')
+ tc6a.other = tc7a
+ tc5a.another = tc7a
+ tc5b = TestSO5.new(name='b')
+ tc5c = TestSO5.new(name='c')
+ tc6b = TestSO6.new(name='3')
+ tc5c.other = tc6b
+ self.assertEqual(TestSO5.select().count(), 3)
+ self.assertEqual(TestSO6.select().count(), 2)
+ self.assertEqual(TestSO7.select().count(), 1)
+ TestSO7.dropTable(cascade=True)
+ self.assertEqual(TestSO5.select().count(), 3)
+ self.assertEqual(TestSO6.select().count(), 2)
+ tc6a.destroySelf()
+ self.assertEqual(TestSO5.select().count(), 2)
+ self.assertEqual(TestSO6.select().count(), 1)
+ tc6b.destroySelf()
+ self.assertEqual(TestSO5.select().count(), 1)
+ self.assertEqual(TestSO6.select().count(), 0)
+ self.assertEqual(iter(TestSO5.select()).next(), tc5b)
+ tc6c = TestSO6.new(name='3')
+ tc5b.other = tc6c
+ self.assertEqual(TestSO5.select().count(), 1)
+ self.assertEqual(TestSO6.select().count(), 1)
+ tc6c.destroySelf()
+ self.assertEqual(TestSO5.select().count(), 0)
+ self.assertEqual(TestSO6.select().count(), 0)
+
+ class TestSO8(SQLObject):
+ name = StringCol(length=10, dbName='name_col')
+ other = ForeignKey('TestSO9', default=None, cascade=False)
+
+ class TestSO9(SQLObject):
+ name = StringCol(length=10, dbName='name_col')
+
+ class TestCase89(SQLObjectTest):
+
+ classes = [TestSO9, TestSO8]
+
+ def testForeignKeyDestroySelfRestrict(self):
+ tc8a = TestSO8.new(name='a')
+ tc9a = TestSO9.new(name='1')
+ tc8a.other = tc9a
+ tc8b = TestSO8.new(name='b')
+ tc9b = TestSO9.new(name='2')
+ self.assertEqual(tc8a.other, tc9a)
+ self.assertEqual(tc8a.otherID, tc9a.id)
+ self.assertEqual(TestSO8.select().count(), 2)
+ self.assertEqual(TestSO9.select().count(), 2)
+ self.assertRaises(Exception, tc9a.destroySelf)
+ tc9b.destroySelf()
+ self.assertEqual(TestSO8.select().count(), 2)
+ self.assertEqual(TestSO9.select().count(), 1)
+ tc8a.destroySelf()
+ tc8b.destroySelf()
+ tc9a.destroySelf()
+ self.assertEqual(TestSO8.select().count(), 0)
+ self.assertEqual(TestSO9.select().count(), 0)
########################################
|
|
From: <dre...@us...> - 2003-12-04 16:44:08
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv16262/SQLObject
Modified Files:
Col.py DBConnection.py SQLObject.py
Log Message:
Merge cascadegeddon-branch. This implements foreign key constraints (postgresql-only) and 'DELETE CASCADE'/'DELETE RESTRICT' (should work on all db's as it's done manually). We don't support 'UPDATE CASCADE'/'UPDATE RESTRICT' yet. To get it working, just pass the 'cascade' keyword argument to ForeignKey. True=CASCADE, False=RESTRICT, None(default)=The old behavior.
Index: Col.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/Col.py,v
retrieving revision 1.33
retrieving revision 1.34
diff -C2 -d -r1.33 -r1.34
*** Col.py 4 Dec 2003 16:36:16 -0000 1.33
--- Col.py 4 Dec 2003 16:44:04 -0000 1.34
***************
*** 34,38 ****
columnDef=None,
validator=None,
! immutable=False):
# This isn't strictly true, since we *could* use backquotes or
--- 34,39 ----
columnDef=None,
validator=None,
! immutable=False,
! cascade=None):
# This isn't strictly true, since we *could* use backquotes or
***************
*** 50,53 ****
--- 51,60 ----
self.immutable = immutable
+ # cascade can be one of:
+ # None: no constraint is generated
+ # True: a CASCADE constraint is generated
+ # False: a RESTRICT constraint is generated
+ self.cascade = cascade
+
if type(constraints) not in (type([]), type(())):
constraints = [constraints]
***************
*** 348,351 ****
--- 355,377 ----
kw['name'] = style.instanceAttrToIDAttr(kw['name'])
SOKeyCol.__init__(self, **kw)
+
+ def postgresCreateSQL(self):
+ from SQLObject import findClass
+ sql = SOKeyCol.postgresCreateSQL(self)
+ if self.cascade is not None:
+ other = findClass(self.foreignKey)
+ tName = other._table
+ idName = other._idName
+ action = self.cascade and 'CASCADE' or 'RESTRICT'
+ constraint = ('CONSTRAINT %(tName)s_exists '
+ 'FOREIGN KEY(%(colName)s) '
+ 'REFERENCES %(tName)s(%(idName)s) '
+ 'ON DELETE %(action)s' %
+ {'tName':tName,
+ 'colName':self.dbName,
+ 'idName':idName,
+ 'action':action})
+ sql = ', '.join([sql, constraint])
+ return sql
class ForeignKey(KeyCol):
Index: DBConnection.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/DBConnection.py,v
retrieving revision 1.56
retrieving revision 1.57
diff -C2 -d -r1.56 -r1.57
*** DBConnection.py 4 Dec 2003 16:36:16 -0000 1.56
--- DBConnection.py 4 Dec 2003 16:44:04 -0000 1.57
***************
*** 280,284 ****
assert 0, "Implement in subclasses"
! def dropTable(self, tableName):
self.query("DROP TABLE %s" % tableName)
--- 280,284 ----
assert 0, "Implement in subclasses"
! def dropTable(self, tableName, cascade=False):
self.query("DROP TABLE %s" % tableName)
***************
*** 614,617 ****
--- 614,621 ----
return '%s SERIAL PRIMARY KEY' % soClass._idName
+ def dropTable(self, tableName, cascade=False):
+ self.query("DROP TABLE %s %s" % (tableName,
+ cascade and 'CASCADE' or ''))
+
def joinSQLType(self, join):
return 'INT NOT NULL'
***************
*** 982,986 ****
column.firebirdCreateSQL()))
! def dropTable(self, tableName):
self.query("DROP TABLE %s" % tableName)
self.query("DROP GENERATOR GEN_%s" % tableName)
--- 986,990 ----
column.firebirdCreateSQL()))
! def dropTable(self, tableName, cascade=False):
self.query("DROP TABLE %s" % tableName)
self.query("DROP GENERATOR GEN_%s" % tableName)
***************
*** 1218,1222 ****
self._meta["%s.id" % soClass._table] = "1"
! def dropTable(self, tableName):
try:
del self._meta["%s.id" % tableName]
--- 1222,1226 ----
self._meta["%s.id" % soClass._table] = "1"
! def dropTable(self, tableName, cascade=False):
try:
del self._meta["%s.id" % tableName]
Index: SQLObject.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/SQLObject.py,v
retrieving revision 1.64
retrieving revision 1.65
diff -C2 -d -r1.64 -r1.65
*** SQLObject.py 12 Nov 2003 17:06:00 -0000 1.64
--- SQLObject.py 4 Dec 2003 16:44:04 -0000 1.65
***************
*** 37,40 ****
--- 37,41 ----
class SQLObjectNotFound(LookupError): pass
+ class SQLObjectIntegrityError(Exception): pass
True, False = 1==1, 0==1
***************
*** 312,315 ****
--- 313,329 ----
return classRegistry[registry][name]
+ def findDependencies(name, registry=None):
+ depends = []
+ for n, klass in classRegistry[registry].items():
+ if findDependantColumns(name, klass):
+ depends.append(klass)
+ return depends
+
+ def findDependantColumns(name, klass):
+ depends = []
+ for col in klass._SO_columns:
+ if col.foreignKey == name and col.cascade is not None:
+ depends.append(col)
+ return depends
class CreateNewSQLObject:
***************
*** 933,936 ****
--- 947,954 ----
_SO_fetchAlternateID = classmethod(_SO_fetchAlternateID)
+ def _SO_depends(cls):
+ return findDependencies(cls.__name__, cls._registry)
+ _SO_depends = classmethod(_SO_depends)
+
def select(cls, clause=None, clauseTables=None,
orderBy=NoDefault, limit=None,
***************
*** 952,959 ****
# 3-03 @@: Should these have a connection argument?
! def dropTable(cls, ifExists=False, dropJoinTables=True):
if ifExists and not cls._connection.tableExists(cls._table):
return
! cls._connection.dropTable(cls._table)
if dropJoinTables:
cls.dropJoinTables(ifExists=ifExists)
--- 970,977 ----
# 3-03 @@: Should these have a connection argument?
! def dropTable(cls, ifExists=False, dropJoinTables=True, cascade=False):
if ifExists and not cls._connection.tableExists(cls._table):
return
! cls._connection.dropTable(cls._table, cascade)
if dropJoinTables:
cls.dropJoinTables(ifExists=ifExists)
***************
*** 1010,1013 ****
--- 1028,1054 ----
def destroySelf(self):
# Kills this object. Kills it dead!
+ depends = []
+ klass = self.__class__
+ depends = self._SO_depends()
+ for k in depends:
+ cols = findDependantColumns(klass.__name__, k)
+ query = []
+ restrict = False
+ for col in cols:
+ if col.cascade == False:
+ # Found a restriction
+ restrict = True
+ query.append("%s = %s" % (col.dbName, self.id))
+ query = ' OR '.join(query)
+ results = k.select(query)
+ if restrict and results.count():
+ # Restrictions only apply if there are
+ # matching records on the related table
+ raise SQLObjectIntegrityError, (
+ "Tried to delete %s::%s but "
+ "table %s has a restriction against it" %
+ (klass.__name__, self.id, k.__name__))
+ for row in results:
+ row.destroySelf()
self._SO_obsolete = True
self._connection._SO_delete(self)
|
|
From: <dre...@us...> - 2003-12-04 16:36:21
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv14756/SQLObject
Modified Files:
Col.py DBConnection.py
Log Message:
Basic Sybase Support, was living in a branch for some long time. Should pass most, but not all tests
Index: Col.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/Col.py,v
retrieving revision 1.32
retrieving revision 1.33
diff -C2 -d -r1.32 -r1.33
*** Col.py 12 Nov 2003 17:04:55 -0000 1.32
--- Col.py 4 Dec 2003 16:36:16 -0000 1.33
***************
*** 182,188 ****
return ''
! def _firebirdType(self):
return self._sqlType()
def mysqlCreateSQL(self):
--- 182,190 ----
return ''
! def _sybaseType(self):
return self._sqlType()
+ def _firebirdType(self):
+ return self._sqlType()
def mysqlCreateSQL(self):
***************
*** 195,198 ****
--- 197,203 ----
return ' '.join([self.dbName, self._sqliteType()] + self._extraSQL())
+ def sybaseCreateSQL(self):
+ return ' '.join([self.dbName, self._sybaseType()] + self._extraSQL())
+
def firebirdCreateSQL(self):
# Ian Sparks pointed out that fb is picky about the order
***************
*** 374,377 ****
--- 379,385 ----
return self._postgresType()
+ def _sybaseType(self):
+ return self._postgresType()
+
def _firebirdType(self):
return self._postgresType()
***************
*** 391,396 ****
--- 399,424 ----
return 'TIMESTAMP'
+ def _sybaseType(self):
+ return self._postgresType()
+
class DateTimeCol(Col):
baseClass = SODateTimeCol
+
+ class SODateCol(SOCol):
+
+ # 3-03 @@: provide constraints; right now we let the database
+ # do any parsing and checking. And DATE and TIME?
+
+ def _mysqlType(self):
+ return 'DATE'
+
+ def _postgresType(self):
+ return 'DATE'
+
+ def _sybaseType(self):
+ return self._postgresType()
+
+ class DateCol(Col):
+ baseClass = SODateCol
class SODecimalCol(SOCol):
Index: DBConnection.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/DBConnection.py,v
retrieving revision 1.55
retrieving revision 1.56
diff -C2 -d -r1.55 -r1.56
*** DBConnection.py 12 Nov 2003 17:06:34 -0000 1.55
--- DBConnection.py 4 Dec 2003 16:36:16 -0000 1.56
***************
*** 24,32 ****
sqlite = None
kinterbasdb = None
warnings.filterwarnings("ignore", "DB-API extension cursor.lastrowid used")
__all__ = ['MySQLConnection', 'PostgresConnection', 'SQLiteConnection',
! 'DBMConnection', 'FirebirdConnection']
_connections = {}
--- 24,33 ----
sqlite = None
kinterbasdb = None
+ Sybase = None
warnings.filterwarnings("ignore", "DB-API extension cursor.lastrowid used")
__all__ = ['MySQLConnection', 'PostgresConnection', 'SQLiteConnection',
! 'DBMConnection', 'FirebirdConnection', 'SybaseConnection']
_connections = {}
***************
*** 431,434 ****
--- 432,439 ----
self._dbConnection.releaseConnection(self._connection)
+ ########################################
+ ## MySQL connection
+ ########################################
+
class MySQLConnection(DBAPI):
***************
*** 528,531 ****
--- 533,539 ----
return Col.Col, {}
+ ########################################
+ ## Postgres connection
+ ########################################
class PostgresConnection(DBAPI):
***************
*** 683,686 ****
--- 691,698 ----
+ ########################################
+ ## SQLite connection
+ ########################################
+
class SQLiteConnection(DBAPI):
***************
*** 745,748 ****
--- 757,870 ----
########################################
+ ## Sybase connection
+ ########################################
+
+ class SybaseConnection(DBAPI):
+
+ def __init__(self, db, user, passwd='', host='localhost',
+ autoCommit=0, **kw):
+ global Sybase
+ if Sybase is None:
+ import Sybase
+ from Sybase import NumericType
+ from Converters import registerConverter, IntConverter
+ registerConverter(NumericType, IntConverter)
+ if not autoCommit and not kw.has_key('pool'):
+ # Pooling doesn't work with transactions...
+ kw['pool'] = 0
+ self.autoCommit=autoCommit
+ self.host = host
+ self.db = db
+ self.user = user
+ self.passwd = passwd
+ DBAPI.__init__(self, **kw)
+
+ def insert_id(self, conn):
+ """
+ Sybase adapter/cursor does not support the
+ insert_id method.
+ """
+ c = conn.cursor()
+ c.execute('SELECT @@IDENTITY')
+ return c.fetchone()[0]
+
+ def makeConnection(self):
+ return Sybase.connect(self.host, self.user, self.passwd,
+ database=self.db, auto_commit=self.autoCommit)
+
+ def _queryInsertID(self, conn, table, idName, names, values):
+ c = conn.cursor()
+ q = self._insertSQL(table, names, values)
+ if self.debug:
+ print 'QueryIns: %s' % q
+ c.execute(q)
+ return self.insert_id(conn)
+
+ def _queryAddLimitOffset(self, query, start, end):
+ if not start:
+ return "%s LIMIT %i" % (query, end)
+ if not end:
+ return "%s LIMIT %i, -1" % (query, start)
+ return "%s LIMIT %i, %i" % (query, start, end-start)
+
+ def createColumn(self, soClass, col):
+ return col.sybaseCreateSQL()
+
+ def createIDColumn(self, soClass):
+ #return '%s INT PRIMARY KEY AUTO_INCREMENT' % soClass._idName
+ return '%s NUMERIC(18,0) IDENTITY' % soClass._idName
+
+ def joinSQLType(self, join):
+ return 'NUMERIC(18,0) NOT NULL' #INT NOT NULL'
+
+ SHOW_TABLES="SELECT name FROM sysobjects WHERE type='U'"
+ def tableExists(self, tableName):
+ for (table,) in self.queryAll(self.SHOW_TABLES):
+ if table.lower() == tableName.lower():
+ return True
+ return False
+
+ def addColumn(self, tableName, column):
+ self.query('ALTER TABLE %s ADD COLUMN %s' %
+ (tableName,
+ column.sybaseCreateSQL()))
+
+ def delColumn(self, tableName, column):
+ self.query('ALTER TABLE %s DROP COLUMN %s' %
+ (tableName,
+ column.dbName))
+
+ SHOW_COLUMNS=("select 'column' = COL_NAME(id, colid) "
+ "from syscolumns where id = OBJECT_ID(%s)")
+ def columnsFromSchema(self, tableName, soClass):
+ colData = self.queryAll(self.SHOW_COLUMNS
+ % tableName)
+ results = []
+ for field, t, nullAllowed, key, default, extra in colData:
+ if field == 'id':
+ continue
+ colClass, kw = self.guessClass(t)
+ kw['name'] = soClass._style.dbColumnToPythonAttr(field)
+ kw['notNone'] = not nullAllowed
+ kw['default'] = default
+ # @@ skip key...
+ # @@ skip extra...
+ results.append(colClass(**kw))
+ return results
+
+ def guessClass(self, t):
+ if t.startswith('int'):
+ return Col.IntCol, {}
+ elif t.startswith('varchar'):
+ return Col.StringCol, {'length': int(t[8:-1])}
+ elif t.startswith('char'):
+ return Col.StringCol, {'length': int(t[5:-1]),
+ 'varchar': False}
+ elif t.startswith('datetime'):
+ return Col.DateTimeCol, {}
+ else:
+ return Col.Col, {}
+
+ ########################################
## Firebird connection
########################################
***************
*** 1019,1022 ****
--- 1141,1148 ----
results.append((id,))
return results
+
+ ########################################
+ ## DBM connection
+ ########################################
class DBMConnection(FileConnection):
|
|
From: <dre...@us...> - 2003-12-04 16:36:21
|
Update of /cvsroot/sqlobject/SQLObject/tests
In directory sc8-pr-cvs1:/tmp/cvs-serv14756/tests
Modified Files:
SQLObjectTest.py test.py
Log Message:
Basic Sybase Support, was living in a branch for some long time. Should pass most, but not all tests
Index: SQLObjectTest.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/tests/SQLObjectTest.py,v
retrieving revision 1.18
retrieving revision 1.19
diff -C2 -d -r1.18 -r1.19
*** SQLObjectTest.py 1 Oct 2003 01:53:48 -0000 1.18
--- SQLObjectTest.py 4 Dec 2003 16:36:17 -0000 1.19
***************
*** 46,49 ****
--- 46,61 ----
return SQLiteConnection('data/sqlite.data')
+
+ 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)
+
def firebirdConnection():
SQLObjectTest.supportDynamic = True
***************
*** 54,61 ****
--- 66,75 ----
user='sysdba', passwd='masterkey')
+
_supportedDatabases = {
'mysql': 'MySQLdb',
'postgres': 'psycopg',
'sqlite': 'sqlite',
+ 'sybase': 'Sybase',
'firebird': 'kinterbasdb',
}
***************
*** 105,109 ****
elif hasattr(c, 'dropTable'):
c.dropTable(ifExists=True)
!
if hasattr(c, '%sCreate' % self.databaseName):
if not __connection__.tableExists(c._table):
--- 119,123 ----
elif hasattr(c, 'dropTable'):
c.dropTable(ifExists=True)
!
if hasattr(c, '%sCreate' % self.databaseName):
if not __connection__.tableExists(c._table):
Index: test.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/tests/test.py,v
retrieving revision 1.34
retrieving revision 1.35
diff -C2 -d -r1.34 -r1.35
*** test.py 4 Nov 2003 02:28:35 -0000 1.34
--- test.py 4 Dec 2003 16:36:17 -0000 1.35
***************
*** 225,228 ****
--- 225,229 ----
########################################
+
class DeleteSelectTest(TestCase1):
***************
*** 235,238 ****
--- 236,240 ----
self.assertEqual(list(TestSO1.select('all')), [])
+
########################################
## Transaction test
***************
*** 455,458 ****
--- 457,471 ----
"""
+ sybaseCreate = """
+ CREATE TABLE auto_test (
+ id integer,
+ first_name VARCHAR(100),
+ last_name VARCHAR(200) NOT NULL,
+ age INT DEFAULT 0,
+ created VARCHAT(40) NOT NULL,
+ happy char(1) DEFAULT 'Y' NOT NULL
+ )
+ """
+
mysqlDrop = """
DROP TABLE IF EXISTS auto_test
***************
*** 460,463 ****
--- 473,480 ----
postgresDrop = """
+ DROP TABLE auto_test
+ """
+
+ sybaseDrop = """
DROP TABLE auto_test
"""
|