sqlobject-cvs Mailing List for SQLObject (Page 191)
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: <ian...@us...> - 2003-04-10 04:46:57
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv2335/SQLObject
Modified Files:
Col.py
Log Message:
Add `unique` keyword argument to Col. (need doc)
Index: Col.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/Col.py,v
retrieving revision 1.7
retrieving revision 1.8
diff -C2 -d -r1.7 -r1.8
*** Col.py 9 Apr 2003 04:35:32 -0000 1.7
--- Col.py 10 Apr 2003 04:46:54 -0000 1.8
***************
*** 22,26 ****
foreignKey=None,
alternateID=False, alternateMethodName=None,
! constraints=None, notNull=False):
# This isn't strictly true, since we *could* use backquotes
# around column names, but why would anyone *want* to
--- 22,27 ----
foreignKey=None,
alternateID=False, alternateMethodName=None,
! constraints=None, notNull=False,
! unique=NoDefault):
# This isn't strictly true, since we *could* use backquotes
# around column names, but why would anyone *want* to
***************
*** 62,65 ****
--- 63,70 ----
constraints = [Constraints.notNull] + constraints
self.contraints = constraints
+ if unique is NoDefault:
+ self.unique = alternateID
+ else:
+ self.unique = unique
def autoConstraints(self):
***************
*** 109,113 ****
if self.notNull:
result.append('NOT NULL')
! if self.alternateID:
result.append('UNIQUE')
return result
--- 114,118 ----
if self.notNull:
result.append('NOT NULL')
! if self.unique:
result.append('UNIQUE')
return result
|
|
From: <ian...@us...> - 2003-04-10 04:46:26
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv2213/SQLObject
Modified Files:
DBConnection.py
Log Message:
Make postgres use SERIAL KEY for id fields
Index: DBConnection.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/DBConnection.py,v
retrieving revision 1.15
retrieving revision 1.16
diff -C2 -d -r1.15 -r1.16
*** DBConnection.py 8 Apr 2003 09:05:19 -0000 1.15
--- DBConnection.py 10 Apr 2003 04:46:15 -0000 1.16
***************
*** 456,460 ****
def createIDColumn(self, soClass):
! return '%s SERIAL' % soClass._idName
def joinSQLType(self, join):
--- 456,460 ----
def createIDColumn(self, soClass):
! return '%s SERIAL PRIMARY KEY' % soClass._idName
def joinSQLType(self, join):
|
|
From: <ian...@us...> - 2003-04-09 04:35:35
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv25006/SQLObject
Modified Files:
Col.py
Log Message:
Made foreign key column names slightly more permissive (you can
use lower case "id")
Index: Col.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/Col.py,v
retrieving revision 1.6
retrieving revision 1.7
diff -C2 -d -r1.6 -r1.7
*** Col.py 8 Apr 2003 02:51:49 -0000 1.6
--- Col.py 9 Apr 2003 04:35:32 -0000 1.7
***************
*** 50,54 ****
self.foreignKey = foreignKey
if foreignKey:
! assert name.endswith('ID'), "All foreign key columns must end with 'ID' (%s)" % repr(name)
self.foreignName = name[:-2]
else:
--- 50,54 ----
self.foreignKey = foreignKey
if foreignKey:
! assert name.upper().endswith('ID'), "All foreign key columns must end with 'ID' (%s)" % repr(name)
self.foreignName = name[:-2]
else:
|
|
From: <ian...@us...> - 2003-04-09 04:34:48
|
Update of /cvsroot/sqlobject/SQLObject/docs
In directory sc8-pr-cvs1:/tmp/cvs-serv24886/docs
Modified Files:
SQLObject.txt
Log Message:
typo
Index: SQLObject.txt
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/docs/SQLObject.txt,v
retrieving revision 1.8
retrieving revision 1.9
diff -C2 -d -r1.8 -r1.9
*** SQLObject.txt 8 Apr 2003 03:08:02 -0000 1.8
--- SQLObject.txt 9 Apr 2003 04:34:45 -0000 1.9
***************
*** 171,175 ****
_table = 'people'
! _id = 'peopleID'
_columns = [Col('firstName', dbName='fname'),
Col('middleInitial', dbName='MI'),
--- 171,175 ----
_table = 'people'
! _idName = 'peopleID'
_columns = [Col('firstName', dbName='fname'),
Col('middleInitial', dbName='MI'),
|
|
From: <ian...@us...> - 2003-04-08 17:25:11
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv24013/SQLObject
Modified Files:
Cache.py SQLObject.py
Log Message:
Fixed purge
Index: Cache.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/Cache.py,v
retrieving revision 1.2
retrieving revision 1.3
diff -C2 -d -r1.2 -r1.3
*** Cache.py 30 Mar 2003 01:47:35 -0000 1.2
--- Cache.py 8 Apr 2003 17:25:03 -0000 1.3
***************
*** 152,153 ****
--- 152,159 ----
self.caches[cls.__name__] = CacheFactory(*self.args, **self.kw)
self.caches[cls.__name__].created(id, obj)
+
+ def purge(self, id, cls):
+ try:
+ self.caches[cls.__name__].purge(id)
+ except KeyError:
+ pass
Index: SQLObject.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/SQLObject.py,v
retrieving revision 1.17
retrieving revision 1.18
diff -C2 -d -r1.17 -r1.18
*** SQLObject.py 8 Apr 2003 03:01:34 -0000 1.17
--- SQLObject.py 8 Apr 2003 17:25:04 -0000 1.18
***************
*** 829,833 ****
#self._SO_delete(self)
self._connection._SO_delete(self)
! self._connection.cache.purge(self.id)
def __repr__(self):
--- 829,833 ----
#self._SO_delete(self)
self._connection._SO_delete(self)
! self._connection.cache.purge(self.id, self.__class__)
def __repr__(self):
|
|
From: <ian...@us...> - 2003-04-08 09:05:23
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv27668/SQLObject
Modified Files:
DBConnection.py
Log Message:
Made fcntl not a requirement (it's not available on Windows)
Index: DBConnection.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/DBConnection.py,v
retrieving revision 1.14
retrieving revision 1.15
diff -C2 -d -r1.14 -r1.15
*** DBConnection.py 7 Apr 2003 19:48:41 -0000 1.14
--- DBConnection.py 8 Apr 2003 09:05:19 -0000 1.15
***************
*** 5,9 ****
from Cache import CacheSet
import Col
! import fcntl
try:
import cPickle as pickle
--- 5,12 ----
from Cache import CacheSet
import Col
! try:
! import fcntl
! except ImportError:
! fcntl = None
try:
import cPickle as pickle
|
|
From: <ian...@us...> - 2003-04-08 09:05:00
|
Update of /cvsroot/sqlobject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv27524
Modified Files:
setup.py
Log Message:
distribution notes
Index: setup.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/setup.py,v
retrieving revision 1.7
retrieving revision 1.8
diff -C2 -d -r1.7 -r1.8
*** setup.py 8 Apr 2003 04:07:08 -0000 1.7
--- setup.py 8 Apr 2003 09:04:55 -0000 1.8
***************
*** 32,33 ****
--- 32,38 ----
download_url="http://prdownloads.sourceforge.net/sqlobject/SQLObject-0.3.tar.gz?download")
+ # Send announce to:
+ # sql...@li...
+ # db...@py...
+ # pyt...@py...
+ # pyt...@py...
|
|
From: <ian...@us...> - 2003-04-08 04:07:11
|
Update of /cvsroot/sqlobject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv28202
Modified Files:
setup.py
Log Message:
Fixed typo in trove
Index: setup.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/setup.py,v
retrieving revision 1.6
retrieving revision 1.7
diff -C2 -d -r1.6 -r1.7
*** setup.py 8 Apr 2003 03:26:29 -0000 1.6
--- setup.py 8 Apr 2003 04:07:08 -0000 1.7
***************
*** 18,22 ****
""",
classifiers=["Development Status :: 4 - Beta",
! "Indended Audience :: Developers",
"License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
"Programming Language :: Python",
--- 18,22 ----
""",
classifiers=["Development Status :: 4 - Beta",
! "Intended Audience :: Developers",
"License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
"Programming Language :: Python",
|
|
From: <ian...@us...> - 2003-04-08 04:05:08
|
Update of /cvsroot/sqlobject/SOWeb In directory sc8-pr-cvs1:/tmp/cvs-serv27207 Modified Files: index.html index.txt Log Message: Updated for 0.3 Index: index.html =================================================================== RCS file: /cvsroot/sqlobject/SOWeb/index.html,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** index.html 13 Mar 2003 20:45:38 -0000 1.3 --- index.html 8 Apr 2003 04:05:04 -0000 1.4 *************** *** 106,116 **** <div class="section" id="download"> <h1><a class="toc-backref" href="#id12" name="download">Download</a></h1> ! <p><a class="reference" href="http://prdownloads.sourceforge.net/sqlobject/SQLObject-0.2.1.tar.gz?download">SQLObject-0.2.1.tar.gz</a></p> ! <p><a class="reference" href="http://prdownloads.sourceforge.net/sqlobject/SQLObject-0.2.1-1.noarch.rpm?download">SQLObject-0.2.1-1.noarch.rpm</a></p> ! <p><a class="reference" href="http://prdownloads.sourceforge.net/sqlobject/SQLObject-0.2.1.win32.exe?download">SQLObject-0.2.1.win32.exe</a></p> </div> <div class="section" id="documentation"> <h1><a class="toc-backref" href="#id13" name="documentation">Documentation</a></h1> ! <p><a class="reference" href="docs/News.html">New in 0.2.1</a></p> <p><a class="reference" href="docs/SQLObject.html">SQLObject documentation</a></p> <p><a class="reference" href="docs/SQLBuilder.html">SQLBuilder documentation</a></p> --- 106,116 ---- <div class="section" id="download"> <h1><a class="toc-backref" href="#id12" name="download">Download</a></h1> ! <p><a class="reference" href="http://prdownloads.sourceforge.net/sqlobject/SQLObject-0.3.tar.gz?download">SQLObject-0.3.tar.gz</a></p> ! <p><a class="reference" href="http://prdownloads.sourceforge.net/sqlobject/SQLObject-0.3-1.noarch.rpm?download">SQLObject-0.3-1.noarch.rpm</a></p> ! <p><a class="reference" href="http://prdownloads.sourceforge.net/sqlobject/SQLObject-0.3.win32.exe?download">SQLObject-0.3.win32.exe</a></p> </div> <div class="section" id="documentation"> <h1><a class="toc-backref" href="#id13" name="documentation">Documentation</a></h1> ! <p><a class="reference" href="docs/News.html">New in 0.3</a></p> <p><a class="reference" href="docs/SQLObject.html">SQLObject documentation</a></p> <p><a class="reference" href="docs/SQLBuilder.html">SQLBuilder documentation</a></p> Index: index.txt =================================================================== RCS file: /cvsroot/sqlobject/SOWeb/index.txt,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** index.txt 13 Mar 2003 20:44:21 -0000 1.2 --- index.txt 8 Apr 2003 04:05:04 -0000 1.3 *************** *** 107,126 **** ======== ! SQLObject-0.2.1.tar.gz__ ! __ http://prdownloads.sourceforge.net/sqlobject/SQLObject-0.2.1.tar.gz?download ! SQLObject-0.2.1-1.noarch.rpm__ ! __ http://prdownloads.sourceforge.net/sqlobject/SQLObject-0.2.1-1.noarch.rpm?download ! SQLObject-0.2.1.win32.exe__ ! __ http://prdownloads.sourceforge.net/sqlobject/SQLObject-0.2.1.win32.exe?download Documentation ============= ! `New in 0.2.1`__ __ docs/News.html --- 107,126 ---- ======== ! SQLObject-0.3.tar.gz__ ! __ http://prdownloads.sourceforge.net/sqlobject/SQLObject-0.3.tar.gz?download ! SQLObject-0.3-1.noarch.rpm__ ! __ http://prdownloads.sourceforge.net/sqlobject/SQLObject-0.3-1.noarch.rpm?download ! SQLObject-0.3.win32.exe__ ! __ http://prdownloads.sourceforge.net/sqlobject/SQLObject-0.3.win32.exe?download Documentation ============= ! `New in 0.3`__ __ docs/News.html |
|
From: <ian...@us...> - 2003-04-08 03:26:32
|
Update of /cvsroot/sqlobject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv17293
Modified Files:
setup.py
Log Message:
Added more information (for PyPI)
Index: setup.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/setup.py,v
retrieving revision 1.5
retrieving revision 1.6
diff -C2 -d -r1.5 -r1.6
*** setup.py 8 Apr 2003 03:08:00 -0000 1.5
--- setup.py 8 Apr 2003 03:26:29 -0000 1.6
***************
*** 1,10 ****
from distutils.core import setup
setup(name="SQLObject",
version="0.3",
! description="Object-Relational Manager",
author="Ian Bicking",
author_email="ia...@co...",
! url="http://www.colorstudy.com/software/SQLObject/",
license="LGPL",
! packages=["SQLObject"])
--- 1,33 ----
from distutils.core import setup
+
+ import sys
+ # patch distutils if it can't cope with the "classifiers" keyword
+ if sys.version < '2.2.3':
+ from distutils.dist import DistributionMetadata
+ DistributionMetadata.classifiers = None
+ DistributionMetadata.download_url = None
+
setup(name="SQLObject",
version="0.3",
! description="Object-Relational Manager, aka database wrapper",
! long_description="""\
! Classes created using SQLObject wrap database rows, presenting a
! friendly-looking Python object instead of a database/SQL interface.
! Emphasizes convenience. Works with MySQL, Postgres, SQLite, and a
! native anydbm-based backend. Requires Python 2.2+.
! """,
! classifiers=["Development Status :: 4 - Beta",
! "Indended Audience :: Developers",
! "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
! "Programming Language :: Python",
! "Topic :: Database",
! "Topic :: Database :: Front-Ends",
! "Topic :: Software Development :: Libraries :: Python Modules",
! ],
author="Ian Bicking",
author_email="ia...@co...",
! url="http://sqlobject.org",
license="LGPL",
! packages=["SQLObject"],
! download_url="http://prdownloads.sourceforge.net/sqlobject/SQLObject-0.3.tar.gz?download")
|
|
From: <ian...@us...> - 2003-04-08 03:08:11
|
Update of /cvsroot/sqlobject/SQLObject/docs In directory sc8-pr-cvs1:/tmp/cvs-serv12016/docs Modified Files: SQLObject.txt Log Message: Updated docs (almost ready for release!) Index: SQLObject.txt =================================================================== RCS file: /cvsroot/sqlobject/SQLObject/docs/SQLObject.txt,v retrieving revision 1.7 retrieving revision 1.8 diff -C2 -d -r1.7 -r1.8 *** SQLObject.txt 3 Apr 2003 19:36:59 -0000 1.7 --- SQLObject.txt 8 Apr 2003 03:08:02 -0000 1.8 *************** *** 1,4 **** ``````````````` ! SQLObject 0.2.1 ``````````````` --- 1,4 ---- ``````````````` ! SQLObject 0.3 ``````````````` *************** *** 9,13 **** SQLObject is by Ian Bicking, <ia...@co...>. The website ! is http://colorstudy.com/software/SQLObject The code is licensed under the `Lesser General Public License`_ --- 9,13 ---- SQLObject is by Ian Bicking, <ia...@co...>. The website ! is http://sqlobject.org The code is licensed under the `Lesser General Public License`_ *************** *** 29,51 **** In using SQLObject, you will create a class definition that will ! describe how the object connects to the database (in addition to any ! other methods you may wish to add to the class). SQLObject will ! produce the code to access the database, and update the database with ! your changes. The interface to the database is meant to be ! indistinguishable from other interfaces you may add to the object. ! SQLObject also includes a novel feature to generate WHERE clauses ! using Python syntax and objects (intead of generating SQL using string ! substitution, as is traditional). Requirements ============ ! Currently SQLObject supports MySQL, PostgreSQL (via ``psycopg``) and ! SQLite. The SQLite support is the youngest. ! Python 2.2 or higher is strictly required. SQLObject uses metaclasses ! to create the objects and properties throughout, and those features ! are only available in 2.2+. Compared To Other Database Wrappers --- 29,53 ---- In using SQLObject, you will create a class definition that will ! describe how the object translates to the database table. SQLObject ! will produce the code to access the database, and update the database ! with your changes. The generated interface looks similar to any other ! interface, and callers need not be aware of the database backend. ! SQLObject also includes a novel feature to avoid generating most SQL. ! Queries are constructed using Python syntax, which is turned into SQL. ! This also allows non-SQL databases to be used with the same query ! syntax. Requirements ============ ! Currently SQLObject supports MySQL, PostgreSQL (via ``psycopg``), ! SQLite, and a DBM-based store. The DBM backend is the youngest, ! and somewhat experimental. ! Python 2.2 or higher is strictly required. No backward compatible ! version is planned -- SQLObject uses many 2.2 features, and in many ! ways is my own learning tool for the dramatically different ! programming style that new-style classes allow. Compared To Other Database Wrappers *************** *** 53,64 **** There are several object-relational mappers (ORM) for Python. I ! honestly can't comment on the quality of those packages, but I'll try ! to place SQLObject in perspective. SQLObject uses new-style classes extensively. The resultant objects ! have that feel as well -- setting attributes has side effects (it ! changes the database), and defining classes has side effects (through ! the use of metaclasses). For me this is something of an experiment in ! new-style classes. I believe it has been successful. SQLObject creates objects that feel similar to normal Python objects --- 55,66 ---- There are several object-relational mappers (ORM) for Python. I ! honestly can't comment deeply on the quality of those packages, but ! I'll try to place SQLObject in perspective. SQLObject uses new-style classes extensively. The resultant objects ! have a new-style feel as a result -- setting attributes has side ! effects (it changes the database), and defining classes has side ! effects (through the use of metaclasses). Attributes are exposed ! without fear, knowing that they can be made dynamic later. SQLObject creates objects that feel similar to normal Python objects *************** *** 71,81 **** This is in contrast to some ORMs that provide a dictionary-like ! interface to the database (for example, PyDo_). The dictionary interface distinguishes the row from a normal Python object. I also don't care for the use of strings where an attribute seems more natural -- columns are limited in number and predefined, just like ! attributes. ! .. _PyDo: http://skunkweb.sourceforge.net/pydo.html SQLObject is, to my knowledge, unique in using metaclasses to --- 73,84 ---- This is in contrast to some ORMs that provide a dictionary-like ! interface to the database (for example, PyDO_). The dictionary interface distinguishes the row from a normal Python object. I also don't care for the use of strings where an attribute seems more natural -- columns are limited in number and predefined, just like ! attributes. (Note: newer version of PyDO apparently allow attribute ! access as well) ! .. _PyDO: http://skunkweb.sourceforge.net/pydo.html SQLObject is, to my knowledge, unique in using metaclasses to *************** *** 84,88 **** XML file (for example, MiddleKit, part of Webware_). By using metaclasses you are able to comfortably define your schema in the ! Python source code. No code generation, no weird tools. .. _Webware: http://webware.sourceforge.net --- 87,92 ---- XML file (for example, MiddleKit, part of Webware_). By using metaclasses you are able to comfortably define your schema in the ! Python source code. No code generation, no weird tools, no ! compilation step. .. _Webware: http://webware.sourceforge.net *************** *** 93,114 **** Here are some things I plan: ! * More databases supported. * Better transaction support -- right now you can use transactions for the database, but the object isn't transaction-aware, so non-database persistence won't be able to be rolled back. ! * Pre-fetching of values for a SQLObject. In particular, when you ! do a `select` that fetches a hundred objects, you ought to fetch ! all those objects in one ``SELECT`` statement, instead of just ! fetching the IDs and issuing one ``SELECT`` for each object. ! * `select` gives an iterator, so all objects don't have to be in ! memory at the same time. ! * Slicing of results, creating ``LIMIT`` and ``OFFSET`` in the SQL. ! * Cache management -- purging old objects, keeping size down, or ! not caching any objects. ! * Maybe even profile some of this, so that I know what really is ! the performance problem (though it also depends greatly on the ! structure of the database). ! * Allow different naming schemes, both for ``id`` and for ! mixedCase/mixed_case. Using SQLObject --- 97,111 ---- Here are some things I plan: ! * More databases supported. SQL databases (like Oracle or Firebird) ! should be easy, non-SQL databases like Metakit should be possible. * Better transaction support -- right now you can use transactions for the database, but the object isn't transaction-aware, so non-database persistence won't be able to be rolled back. ! * Profile SQLObject of this, so that I can identify bottlenecks. ! * Allow different naming schemes. ! * Makes hooks with a validation and form generation package, so ! SQLObject classes (read: schemas) can be published for editing ! more directly and easily. ! * Automatic joins in select queries. Using SQLObject *************** *** 133,142 **** CREATE TABLE person ( ! id INT PRIMARY KEY, first_name VARCHAR(100) NOT NULL, middle_initial CHAR(1), last_name VARCHAR(150) NOT NULL ); - CREATE SEQUENCE person_seq; The Python class will look like this:: --- 130,138 ---- CREATE TABLE person ( ! id SERIAL, first_name VARCHAR(100) NOT NULL, middle_initial CHAR(1), last_name VARCHAR(150) NOT NULL ); The Python class will look like this:: *************** *** 145,150 **** __connection__ = MySQLConnection( ! host='localhost', db='sqlobject_test', ! user='sqlobject_test', passwd='sqltest') class Person(SQLObject): --- 141,145 ---- __connection__ = MySQLConnection( ! host='localhost', db='test', user='test', passwd='') class Person(SQLObject): *************** *** 154,159 **** Col('lastName')] ! The special variable `__connection__` gives the access method for any ! classes in the module. You can also use the class variable `_connection` in the same way. The MySQL connection is shown, for Postgres use ``DBConnection.PostgresConnection(dsnString)``. --- 149,154 ---- Col('lastName')] ! The special variable `__connection__` gives the database connection ! for any classes in the module. You can also use the class variable `_connection` in the same way. The MySQL connection is shown, for Postgres use ``DBConnection.PostgresConnection(dsnString)``. *************** *** 171,197 **** use the keyword `dbName` analogously. If the primary key for the row isn't ``id``, you can use a _idName class attribute (like ! ``_idName='PersonID'``). ! You'll note that each column is a ``Col`` instance. This way you can give extra information about the column -- the first argument is always the name of the column, but you can give default values (for when new instances are created) and indicate it is a foreign key to ! another class/table. More options may be added later (perhaps type ! checking, for instance). ! SQLObject differs from most other systems in that it currently doesn't ! do any type checking. It is assumed that the database does this -- ! the database also usually does type coercion, so that the string ! ``"10"`` will be converted to the integer ``10`` if necessary. This ! should not present any security problem, as all values are properly ! quoted; but perhaps you'll find that bugs aren't caught that could ! be. You can still add extra constraints, if you wish (see ! `Overriding Column Attributes`_). You'll note that the ``id`` column is not given in the class ! definition. The ``id`` column must always exist (and be declared as ! ``INT PRIMARY KEY AUTO_INCREMENT`` in MySQL or ``INT PRIMARY KEY`` in ! Postgres with the accompanying sequence), and so there's no need to ! declare it. Using the Class --- 166,200 ---- use the keyword `dbName` analogously. If the primary key for the row isn't ``id``, you can use a _idName class attribute (like ! ``_idName='PersonID'``). An example using all of these:: ! class Person(SQLObject): ! ! _table = 'people' ! _id = 'peopleID' ! _columns = [Col('firstName', dbName='fname'), ! Col('middleInitial', dbName='MI'), ! Col('lastName', dbName='lname)] ! ! Each column is described with a ``Col`` instance. This way you can give extra information about the column -- the first argument is always the name of the column, but you can give default values (for when new instances are created) and indicate it is a foreign key to ! another class/table. To indicate the type of the column subclasses ! are provided. See `Automatic Schema Generation`_ for more (type ! information is not required, though). ! SQLObject doesn't require any type checking, and though some type ! information can be provided, it's not extensively used. It is assumed ! that the database does the type checking -- the database also usually ! does type coercion, so that the string ``"10"`` will be converted to ! the integer ``10`` if necessary. This should not present any security ! problem, as all values are properly quoted; but perhaps you'll find ! that bugs aren't caught that could be. You can still add extra ! constraints, if you wish (see `Overriding Column Attributes`_). You'll note that the ``id`` column is not given in the class ! definition, it is implied. For MySQL databases it should be defined ! as ``INT PRIMARY KEY AUTO_INCREMENT``, in Postgres ``SERIAL``, and in ! SQLite as ``INTEGER PRIMARY KEY``. Using the Class *************** *** 210,214 **** 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 equivalent of ``None``). When you create an object, it is immediately inserted into the --- 213,217 ---- 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 *************** *** 237,244 **** unique -- there is only one ``Person`` instance of a particular id in memory at any one time (unless you are using transactions_). If you ! ask for more than one, 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). To get an idea of what's happening behind the surface, I'll give the --- 240,248 ---- unique -- there is only one ``Person`` instance of a particular id in memory at any one time (unless you are using transactions_). If you ! ask for more than one 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). To get an idea of what's happening behind the surface, I'll give the *************** *** 266,270 **** UPDATE person SET middle_initial='Q' WHERE id = 1; >>> p.middleInitial ! -- In this case, we've actually saved the info. 'Q' >>> p2 = Person(1) --- 270,275 ---- UPDATE person SET middle_initial='Q' WHERE id = 1; >>> p.middleInitial ! -- In this case, we've actually saved the info, so ! -- no query is made. 'Q' >>> p2 = Person(1) *************** *** 277,292 **** Hopefully you see that the SQL that gets sent is pretty clear and ! predictable. There are slight inefficiencies in inserting rows, but ! this is relatively less common than retrieval anyway. To view the SQL ! being sent, pass the keyword argument ``debug=1`` to MySQLConnection ! or PostgresConnection -- all SQL will be printed to the console. This ! can be reassuring. ! ! If you find yourself retrieving large numbers of these objects the ! performance penalty should not be too great because of the caching. ! However, if you iterate through them each and access them in sequence, ! then many SQL statements will be sent instead of a single statement ! that fetches all the rows. Maybe later that will be changed, but it's ! a performance problem for the time being. As a small optimization, instead of assigning each attribute --- 282,290 ---- Hopefully you see that the SQL that gets sent is pretty clear and ! predictable. There are some inefficiences; these should be worked out ! in time. To view the SQL being sent, pass the keyword argument ! ``debug=1`` to your connection object -- all SQL will be printed to ! the console. This can be reassuring, and I would encourage you to try ! it. As a small optimization, instead of assigning each attribute *************** *** 311,316 **** The list of columns is a list of `Col` objects. These objects don't ! have functionality, but give you a way to specify the column. In the ! future more manners of specification will probably be available. If you find yourself writing ``Col(colName)`` a lot, you might be --- 309,313 ---- The list of columns is a list of `Col` objects. These objects don't ! have functionality, but give you a way to specify the column. If you find yourself writing ``Col(colName)`` a lot, you might be *************** *** 319,324 **** be turned into a `Col` object for you. ! The first argument is always `name` -- the (Pythonic) name of the ! column. The other keyword arguments: `dbName`: --- 316,321 ---- be turned into a `Col` object for you. ! The first argument to `Col` is always `name` -- the (Pythonic) name of ! the column. The other keyword arguments: `dbName`: *************** *** 328,343 **** `default`: The default value for this column. Used when creating a new ! row. If you give a callable object or function, then the result ! of that call will be used. So you can give ``DateTime.now`` to ! make the default value be the current time. Or you can use ! ``func.NOW()`` to have the database use the ! ``NOW()`` function. If you don't give a default there will be an exception if this column isn't specified in the call to `new`. `foreignKey`: ! You can give a class name for `foreignKey`, which means that the ! column references another class (which is another row in another ! table). The column name should end with ``ID``, and the database ! column is expected to end with ``_id``. This will create another ! method in your class. For instance, if the ``personID`` column points to the ``Person`` --- 325,341 ---- `default`: The default value for this column. Used when creating a new ! row. If you give a callable object or function, then on object ! creation the function will be called, and the return value will ! be used. So you can give ``DateTime.now`` to make the default ! value be the current time. Or you can use ! ``SQLBuilder.func.NOW()`` to have the database use the ``NOW()`` ! function internally. If you don't give a default there will be an exception if this column isn't specified in the call to `new`. `foreignKey`: ! You can give a class name (as a string) for `foreignKey`, which ! means that the column references another class (which is another ! row in another table). The column name should end with ``ID``, ! and the database column is expected to end with ``_id``. This ! will create another method in your class. For instance, if the ``personID`` column points to the ``Person`` *************** *** 346,350 **** corresponding ``Person`` instance. ! Pass a *string* with `foreignKey`, not the actual class. `alternateID`: This boolean (default False) indicates if the column can be used --- 344,349 ---- corresponding ``Person`` instance. ! Pass a *string* with `foreignKey`, not the actual class. This ! avoids circular dependencies. `alternateID`: This boolean (default False) indicates if the column can be used *************** *** 352,356 **** class method will be added, like ``byUsername`` which will return that object. User `alternateMethodName` if you don't like the ! ``by*`` algorithm (e.g. ``alternateMethodName="username"``). The column should be declared ``UNIQUE`` in your table schema. --- 351,355 ---- class method will be added, like ``byUsername`` which will return that object. User `alternateMethodName` if you don't like the ! ``by*`` name (e.g. ``alternateMethodName="username"``). The column should be declared ``UNIQUE`` in your table schema. *************** *** 365,369 **** The connection object to use, from `DBConnection`. You can also set the variable `__connection__` in the enclosing module and ! it will be picked up. `_table`: The database name of the table for this class. If you don't give --- 364,369 ---- The connection object to use, from `DBConnection`. You can also set the variable `__connection__` in the enclosing module and ! it will be picked up. You can also pass a connection object in ! at instance creation time, as described in transactions_. `_table`: The database name of the table for this class. If you don't give *************** *** 379,382 **** --- 379,384 ---- then this is probably the only way to do so. You should also use it with transactions_ (it is not implied). + `_idName`: + The name of the primary key column (default ``id``). Relations Between Classes/Tables *************** *** 418,423 **** This will create another attribute ``person``, which will retrieve a ``Person`` object, while ``personID`` will still retrieve the integer ! ID. We use a string instead of a class here because circular ! dependencies will kill us otherwise. We also add an attribute `_joins` to ``Person``. This is a list of --- 420,424 ---- This will create another attribute ``person``, which will retrieve a ``Person`` object, while ``personID`` will still retrieve the integer ! ID. We also add an attribute `_joins` to ``Person``. This is a list of *************** *** 434,437 **** --- 435,440 ---- method. + .. _Many-to-Many: + Relations: Many-to-Many ~~~~~~~~~~~~~~~~~~~~~~~ *************** *** 541,544 **** --- 544,557 ---- selecting is always assumed to be included, of course. + You can use the keyword arguments `orderBy` and `groupBy` to create + ``ORDER BY`` and ``GROUP 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 the special class variable `_defaultOrder` to give a + default ordering for all selects. To get an unordered result when + `_defaultOrder` is used, use ``orderBy=None``. + Select results are generators, which are lazily evaluated. So the SQL is only executed when you iterate over the select results, or if you *************** *** 552,557 **** the end of the SQL query. If the slice cannot be performed in the SQL (e.g., peeps[:-10]), then the select is executed, and the slice is ! performed on the list. This will only happen when you use negative ! indexes. You can get the length of the result without executing the full SQL. --- 565,570 ---- the end of the SQL query. If the slice cannot be performed in the SQL (e.g., peeps[:-10]), then the select is executed, and the slice is ! performed on the list of results. This will only happen when you use ! negative indexes. You can get the length of the result without executing the full SQL. *************** *** 567,572 **** While we haven't done so in the examples, you can include your own ! functionality in the object in the class definition. Including ! methods should be obvious enough (just define them). Initializing the Objects --- 580,586 ---- While we haven't done so in the examples, you can include your own ! methods in the class definition. Including methods should be obvious ! enough (just define them), but there are some other details to be ! aware of. Initializing the Objects *************** *** 575,580 **** 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 it's better to use the ! `_init` method, which is only called once in an objects life (with one argument -- the object's ID). --- 589,594 ---- 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). *************** *** 633,645 **** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ! It's a little complicated, though, if you want to figure out how to ! override the behavior of an attribute. For instance, imagine there's ! special 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. SQLObject creates methods like ``_set_lastName`` for ! each of your columns (but no ``_del_`` or ``_doc_``), but again you ! can't use this, since there's no superclass to reference, and you want ! to override that ``_set_lastName`` method yourself. To deal with this, SQLObject creates two methods for each getter and --- 647,659 ---- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ! It's a little more complicated if you want to override the behavior of ! an attribute. For instance, imagine there's special 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. SQLObject creates ! methods like ``_set_lastName`` for each of your columns (but no ! ``_del_`` or ``_doc_``), but again you can't use this, since there's ! no superclass to reference, and you want to override that ! ``_set_lastName`` method yourself. To deal with this, SQLObject creates two methods for each getter and *************** *** 684,693 **** 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. Hopefully people will get used to this sort of thing, though. Transactions ------------ ! Transaction support for SQLObject is still a little vague. Transactions can be used like:: --- 698,707 ---- 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. Transactions ------------ ! Transaction support in SQLObject is left to the database. Transactions can be used like:: *************** *** 714,737 **** ---------------------------------- ! The `DBConnection` module currently has three classes, `MySQLConnection`, ! `PostgresConnection`, and `SQLiteConnection`. `MySQLConnection` takes the keyword arguments `host`, `db`, `user`, and `passwd`, just like `MySQLdb.connect` does. `PostgresConnection` takes a single connection string, like ``"dbname=something user=some_user"``, just like `psycopg.connect`. ! `SQLiteConnection` takes the a single connection string. ! ! You can pass the keyword argument `debug` to either connector. If set ! to true, then any SQL sent to the database will also be printed to the ! console. SQLite ! ------ ! SQLite doesn't support a great deal of concurrency. For instance, say ! you want to delete a bunch of rows:: for p in Person.select(Person.q.firstName == 'Bob'): --- 728,775 ---- ---------------------------------- ! The `DBConnection` module currently has four external classes, ! `MySQLConnection`, `PostgresConnection`, `SQLiteConnection`, ! and `DBMConnection`. ! ! You can pass the keyword argument `debug` to any connector. If set to ! true, then any SQL sent to the database will also be printed to the ! console. ! ! MySQL ! ~~~~~ `MySQLConnection` takes the keyword arguments `host`, `db`, `user`, and `passwd`, just like `MySQLdb.connect` does. + MySQLConnection supports all the features, though MySQL does not + support transactions_. + + Postgres + ~~~~~~~~ + `PostgresConnection` takes a single connection string, like ``"dbname=something user=some_user"``, just like `psycopg.connect`. + You can also use the same keyword arguments as for `MySQLConnection`, + and a dsn string will be constructed. ! PostgresConnection supports transactions and all other features except ! `automatic class generation`_. SQLite ! ~~~~~~ ! `SQLiteConnection` takes the a single string, which is the path to the ! database file. ! ! SQLite puts all data into one file, with a journal file that is opened ! in the same directory during operation (the file is deleted when the ! program quits). SQLite does not restrict the types you can put in a ! column -- strings can go in integer columns, dates in integers, etc. ! ! SQLiteConnection doesn't support `automatic class generation`_ and ! SQLite does not support `runtime column changes`_ or transactions_. ! ! SQLite doesn't support much concurrency. For instance, say you want ! to delete a bunch of rows:: for p in Person.select(Person.q.firstName == 'Bob'): *************** *** 740,748 **** Because the ``select`` is an iterator, and keeps a connection open, when you do the ``p.destroy()`` you'll get an exception, because ! SQLite doesn't support this concurrency. Instead, do:: for p in list(Person.select(Person.q.firstName == 'Bob')): p.destroy() Exported Symbols ================ --- 778,880 ---- Because the ``select`` is an iterator, and keeps a connection open, when you do the ``p.destroy()`` you'll get an exception, because ! SQLite doesn't support two open connections. Instead, do:: for p in list(Person.select(Person.q.firstName == 'Bob')): p.destroy() + DBMConnection + ~~~~~~~~~~~~~ + + `DBMConnection` takes a single string, which is the path to a + directory in which to store the database. + + DBMConnection uses flat hash databases to store all the data. These + databases are created by the standard `anydbm` module. It's something + of an experiment, and things like safety under concurrent access + (multithreaded or multiprocess) should not be expected. `groupBy` is + also not supported, though select queries, ordering, and slices are + supported. + + DBMConnection allows any kind of objects to be put in columns -- all + values are pickled, and so only normal pickling restrictions apply. + + DBMConnection does not support `automatic class generation` or + transactions_. + + Automatic Schema Generation + --------------------------- + + All the connections support creating and droping tables based on the + class definition. First you have to prepare your class definition, + which means including type information in your columns (though + DBMConnection_ do not require or use type information). + + Columns Types + ~~~~~~~~~~~~~ + + A column type is indicated by using a subclass of `Col`: + + `StringCol`: + StringCol represents ``CHAR``, ``VARCHAR``, and ``TEXT``. + If you pass no `length` keyword argument, then ``TEXT`` will + be assumed. If indicate ``varchar=False`` then ``CHAR`` will + be used, otherwise ``VARCHAR`` is the default. + `IntCol`: + The ``INT`` type. + `FloatCol`: + The ``FLOAT`` type. + `KeyCol`: + A foreign key. Just like `Col` right now... the interface will + be trimmed down eventually. + `EnumCol`: + A MySQL ``ENUM``, i.e., one of a finite number of strings. + For other databases this will be a ``VARCHAR``. + `DateTimeCol`: + ``DATETIME``. + + Creating and Dropping Tables + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + To create a database call `createTable`. It takes two arguments: + + `ifNotExists`: + If the table already exists, then don't try to create it. Default + False. + `createJoinTables`: + If you used Many-to-Many_ relations, then the intermediate tables + will be created (but only for one of the two involved classes). + Default True. + + `dropTable` takes arguments `ifExists` and `dropJoinTables`, + self-explanatory. + + Automatic Class Generation + --------------------------- + + *Currently this is only available for with MySQL_* + + SQLObject can read the table description from the database, and fill + in the class columns (as would normally be described in the `_columns` + attribute). Do this like:: + + class Person(SQLObject): + + _fromDatabase = True + + You can still specify columns (in `_columns`), and only missing + columns will be added. + + Runtime Column Changes + ---------------------- + + *SQLite does not support this feature* + + You can add and remove columns to your class at runtime. Such changes + will effect all instances, since changes are made inplace to the + class. There are two methods, `addColumn` and `delColumn`, both of + which take a `Col` object (or subclass) as an argument. There's also + an option argument `changeSchema` which, if True, will add or drop the + column from the database (typically with an ``ALTER`` command). + Exported Symbols ================ *************** *** 759,768 **** * `RelatedJoin` * `getID` ! * `getObject`. From `DBConnection`: * `MySQLConnection` ! * `PostgresConnection`. From `SQLBuilder`: --- 891,902 ---- * `RelatedJoin` * `getID` ! * `getObject` From `DBConnection`: * `MySQLConnection` ! * `PostgresConnection` ! * `SQLiteConnection` ! * `DBMConnection` From `SQLBuilder`: *************** *** 775,779 **** * `CONTAINSSTRING` * `const` ! * `func`. For more information on SQLBuilder, read the `SQLBuilder --- 909,913 ---- * `CONTAINSSTRING` * `const` ! * `func` For more information on SQLBuilder, read the `SQLBuilder |
|
From: <ian...@us...> - 2003-04-08 03:08:10
|
Update of /cvsroot/sqlobject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv12016
Modified Files:
README.txt setup.py
Log Message:
Updated docs (almost ready for release!)
Index: README.txt
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/README.txt,v
retrieving revision 1.2
retrieving revision 1.3
diff -C2 -d -r1.2 -r1.3
*** README.txt 13 Mar 2003 20:32:43 -0000 1.2
--- README.txt 8 Apr 2003 03:08:00 -0000 1.3
***************
*** 1,5 ****
! ===============
! SQLObject 0.2.1
! ===============
:Author: Ian Bicking
--- 1,5 ----
! =============
! SQLObject 0.3
! =============
:Author: Ian Bicking
***************
*** 12,17 ****
It absolutely requires Python 2.2 or higher. It currently supports
! MySQL through the `MySQLdb` package, and PostgreSQL through the
! `psycopg` package.
For more information please see the documentation in
--- 12,17 ----
It absolutely requires Python 2.2 or higher. It currently supports
! MySQL through the `MySQLdb` package, PostgreSQL through the
! `psycopg` package, SQLite, and a DBM-based backend.
For more information please see the documentation in
Index: setup.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/setup.py,v
retrieving revision 1.4
retrieving revision 1.5
diff -C2 -d -r1.4 -r1.5
*** setup.py 14 Mar 2003 04:17:53 -0000 1.4
--- setup.py 8 Apr 2003 03:08:00 -0000 1.5
***************
*** 1,5 ****
from distutils.core import setup
setup(name="SQLObject",
! version="0.2.1",
description="Object-Relational Manager",
author="Ian Bicking",
--- 1,5 ----
from distutils.core import setup
setup(name="SQLObject",
! version="0.3",
description="Object-Relational Manager",
author="Ian Bicking",
|
|
From: <ian...@us...> - 2003-04-08 03:01:38
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv10413/SQLObject
Modified Files:
SQLObject.py
Log Message:
Changed ifExists argument to createTable to make more sense,
as ifNotExists
Index: SQLObject.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/SQLObject.py,v
retrieving revision 1.16
retrieving revision 1.17
diff -C2 -d -r1.16 -r1.17
*** SQLObject.py 7 Apr 2003 23:28:07 -0000 1.16
--- SQLObject.py 8 Apr 2003 03:01:34 -0000 1.17
***************
*** 778,790 ****
dropTable = classmethod(dropTable)
! def createTable(cls, ifExists=False, createJoinTables=True):
! if ifExists and cls._connection.tableExists(cls._table):
return
cls._connection.createTable(cls)
if createJoinTables:
! cls.createJoinTables(ifExists=ifExists)
createTable = classmethod(createTable)
! def createJoinTables(cls, ifExists=False):
for join in cls._joins:
if not join.hasIntermediateTable():
--- 778,790 ----
dropTable = classmethod(dropTable)
! def createTable(cls, ifNotExists=False, createJoinTables=True):
! if ifNotExists and cls._connection.tableExists(cls._table):
return
cls._connection.createTable(cls)
if createJoinTables:
! cls.createJoinTables(ifNotExists=ifNotExists)
createTable = classmethod(createTable)
! def createJoinTables(cls, ifNotExists=False):
for join in cls._joins:
if not join.hasIntermediateTable():
***************
*** 796,800 ****
if join.callingClass > join.otherClass:
continue
! if ifExists and \
cls._connection.tableExists(join.intermediateTable):
continue
--- 796,800 ----
if join.callingClass > join.otherClass:
continue
! if ifNotExists and \
cls._connection.tableExists(join.intermediateTable):
continue
|
|
From: <ian...@us...> - 2003-04-08 03:01:38
|
Update of /cvsroot/sqlobject/SQLObject/tests
In directory sc8-pr-cvs1:/tmp/cvs-serv10413/tests
Modified Files:
SQLObjectTest.py
Log Message:
Changed ifExists argument to createTable to make more sense,
as ifNotExists
Index: SQLObjectTest.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/tests/SQLObjectTest.py,v
retrieving revision 1.5
retrieving revision 1.6
diff -C2 -d -r1.5 -r1.6
*** SQLObjectTest.py 7 Apr 2003 19:48:42 -0000 1.5
--- SQLObjectTest.py 8 Apr 2003 03:01:33 -0000 1.6
***************
*** 52,56 ****
__connection__.query(c.create)
elif hasattr(c, 'createTable'):
! c.createTable(ifExists=True)
#__connection__.debug = 0
self.inserts()
--- 52,56 ----
__connection__.query(c.create)
elif hasattr(c, 'createTable'):
! c.createTable(ifNotExists=True)
#__connection__.debug = 0
self.inserts()
|
|
From: <ian...@us...> - 2003-04-08 02:51:53
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv7411/SQLObject
Modified Files:
Col.py
Log Message:
Fixed SQLite's type generation
Index: Col.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/Col.py,v
retrieving revision 1.5
retrieving revision 1.6
diff -C2 -d -r1.5 -r1.6
*** Col.py 7 Apr 2003 23:27:02 -0000 1.5
--- Col.py 8 Apr 2003 02:51:49 -0000 1.6
***************
*** 123,128 ****
def _sqliteType(self):
! # SQLite is naturally typeless
! return ''
def mysqlCreateSQL(self):
--- 123,132 ----
def _sqliteType(self):
! # SQLite is naturally typeless, so as a fallback it uses
! # no type.
! try:
! return self._sqlType()
! except ValueError:
! return ''
def mysqlCreateSQL(self):
|
|
From: <ian...@us...> - 2003-04-07 23:28:11
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv8871/SQLObject
Modified Files:
SQLObject.py
Log Message:
Assertion to avoid Col('id')
Index: SQLObject.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/SQLObject.py,v
retrieving revision 1.15
retrieving revision 1.16
diff -C2 -d -r1.15 -r1.16
*** SQLObject.py 7 Apr 2003 19:48:41 -0000 1.15
--- SQLObject.py 7 Apr 2003 23:28:07 -0000 1.16
***************
*** 317,320 ****
--- 317,321 ----
def addColumn(cls, column, changeSchema=False):
name = column.name
+ assert name != 'id', "The 'id' column is implicit, and should not be defined as a column"
cls._SO_columnDict[name] = column
|
|
From: <ian...@us...> - 2003-04-07 23:27:07
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv8439/SQLObject
Modified Files:
Col.py
Log Message:
Default SQLite to typless (i.e., Col can be used with auto schema
generation)
Index: Col.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/Col.py,v
retrieving revision 1.4
retrieving revision 1.5
diff -C2 -d -r1.4 -r1.5
*** Col.py 7 Apr 2003 19:48:40 -0000 1.4
--- Col.py 7 Apr 2003 23:27:02 -0000 1.5
***************
*** 123,127 ****
def _sqliteType(self):
! return self._sqlType()
def mysqlCreateSQL(self):
--- 123,128 ----
def _sqliteType(self):
! # SQLite is naturally typeless
! return ''
def mysqlCreateSQL(self):
|
|
From: <ian...@us...> - 2003-04-07 20:11:32
|
Update of /cvsroot/sqlobject/SQLObject/docs
In directory sc8-pr-cvs1:/tmp/cvs-serv17865
Modified Files:
News.txt
Log Message:
Updated
Index: News.txt
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/docs/News.txt,v
retrieving revision 1.4
retrieving revision 1.5
diff -C2 -d -r1.4 -r1.5
*** News.txt 14 Mar 2003 09:10:11 -0000 1.4
--- News.txt 7 Apr 2003 20:11:28 -0000 1.5
***************
*** 14,21 ****
--------
* New column classes (see `Col` module), indicates type
! * Table creation (SQL schema generation) via new class method
! `createTable`.
* Objects are not cached indefinitely. Cached objects are expired
--- 14,28 ----
--------
+ * Table creation (SQL schema generation) via new class method
+ `createTable`. And of course a `dropTable` method to go with.
+
+ * Add and remove columns at runtime, optionally modifying the
+ schema in the database (via ``ALTER``). (Does not work in
+ SQLite)
+
* New column classes (see `Col` module), indicates type
! * Classes can be created by parsing an already existant table
! (MySQL only).
* Objects are not cached indefinitely. Cached objects are expired
***************
*** 25,28 ****
--- 32,57 ----
pass ``nocache=True`` to your connection object to eliminate as much
caching as possible. See `Cache` module for a bit more.
+
+ * New DBMConnection, implements a database-like backend without any
+ database to speak of, including queries (so long as you use
+ `SQLBuilder` and don't generate your where clauses manually).
+ Actual SQL generation is done entirely by the database connection,
+ allowing portability across very different backends.
+
+ * Postgres table IDs should be created with type ``SERIAL`` (which
+ implicitly creates a sequence).
+
+ * New `_defaultOrder` class variable gives a default for the
+ `orderBy` parameter to `select` queries.
+
+ Bugs
+ ----
+
+ * LIMIT/OFFSET (select result slicing) works in Postgres and SQLite.
+
+ * ``tableExists`` method from DBConnection works in same.
+
+ * mxDateTime not required (never should have been, always just an
+ option).
SQLObject 0.2.1
|
|
From: <ian...@us...> - 2003-04-07 19:49:18
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv3612/SQLObject
Modified Files:
Col.py DBConnection.py SQLObject.py
Log Message:
* Extended the tests to work with Postgres, SQLite, DBM
* Some databases don't implement some features (SQLite doesn't support
dynamic columns, all but MySQL don't support auto-class creation),
at least for now.
* Make SERIAL work for columns under Postgres, as well as _idName
* Fixed LIMIT/OFFSET bugs in Postgres, SQLite
* tableExists in Postgres
Index: Col.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/Col.py,v
retrieving revision 1.3
retrieving revision 1.4
diff -C2 -d -r1.3 -r1.4
*** Col.py 31 Mar 2003 05:51:28 -0000 1.3
--- Col.py 7 Apr 2003 19:48:40 -0000 1.4
***************
*** 122,125 ****
--- 122,128 ----
return self._sqlType()
+ def _sqliteType(self):
+ return self._sqlType()
+
def mysqlCreateSQL(self):
return ' '.join([self.dbName, self._mysqlType()] + self._extraSQL())
***************
*** 190,193 ****
--- 193,200 ----
def _sqliteType(self):
+ return 'INT'
+
+ def _postgresType(self):
+ # @@: there's a better type for this...
return 'INT'
Index: DBConnection.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/DBConnection.py,v
retrieving revision 1.13
retrieving revision 1.14
diff -C2 -d -r1.13 -r1.14
*** DBConnection.py 7 Apr 2003 07:43:36 -0000 1.13
--- DBConnection.py 7 Apr 2003 19:48:41 -0000 1.14
***************
*** 28,32 ****
--- 28,34 ----
sqlite = None
import re
+ import warnings
+ warnings.filterwarnings("ignore", "DB-API extension cursor.lastrowid used")
__all__ = ['MySQLConnection', 'PostgresConnection', 'SQLiteConnection',
***************
*** 118,123 ****
return Transaction(self)
! def queryInsertID(self, table, names, values):
! return self._runWithConnection(self._queryInsertID, table, names, values)
def iterSelect(self, select):
--- 120,125 ----
return Transaction(self)
! def queryInsertID(self, table, idName, names, values):
! return self._runWithConnection(self._queryInsertID, table, idName, names, values)
def iterSelect(self, select):
***************
*** 346,350 ****
user=self.user, passwd=self.passwd)
! def _queryInsertID(self, conn, table, names, values):
c = conn.cursor()
q = self._insertSQL(table, names, values)
--- 348,352 ----
user=self.user, passwd=self.passwd)
! def _queryInsertID(self, conn, table, idName, names, values):
c = conn.cursor()
q = self._insertSQL(table, names, values)
***************
*** 428,436 ****
return psycopg.connect(self.dsn)
! def _queryInsertID(self, conn, table, names, values):
c = conn.cursor()
! c.execute('SELECT nextval(\'%s_seq\')' % table)
(id,) = c.fetchone()
! names.append('id')
values.append(id)
q = self._insertSQL(table, names, values)
--- 430,438 ----
return psycopg.connect(self.dsn)
! def _queryInsertID(self, conn, table, idName, names, values):
c = conn.cursor()
! c.execute('SELECT nextval(\'%s_id_seq\')' % table)
(id,) = c.fetchone()
! names.append(idName)
values.append(id)
q = self._insertSQL(table, names, values)
***************
*** 445,449 ****
if not end:
return "%s OFFSET %i" % (query, start)
! return "%s LIMIT %i OFFSET %i" % (query, start, end-start)
def createColumn(self, soClass, col):
--- 447,451 ----
if not end:
return "%s OFFSET %i" % (query, start)
! return "%s LIMIT %i OFFSET %i" % (query, end-start, start)
def createColumn(self, soClass, col):
***************
*** 458,462 ****
def tableExists(self, tableName):
# @@: obviously broken
! return False
class SQLiteConnection(DBAPI):
--- 460,477 ----
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))
!
class SQLiteConnection(DBAPI):
***************
*** 470,474 ****
return sqlite.connect(self.dsn)
! def _queryInsertID(self, conn, table, names, values):
c = conn.cursor()
q = self._insertSQL(table, names, values)
--- 485,489 ----
return sqlite.connect(self.dsn)
! def _queryInsertID(self, conn, table, idName, names, values):
c = conn.cursor()
q = self._insertSQL(table, names, values)
***************
*** 484,488 ****
if not end:
return "%s OFFSET %i" % (query, start)
! return "%s LIMIT %i OFFSET %i" % (query, start, end-start)
def createColumn(self, soClass, col):
--- 499,503 ----
if not end:
return "%s OFFSET %i" % (query, start)
! return "%s LIMIT %i OFFSET %i" % (query, end-start, start)
def createColumn(self, soClass, col):
***************
*** 492,496 ****
return '%s INTEGER PRIMARY KEY' % soClass._idName
! def joinSQLType(self):
return 'INT NOT NULL'
--- 507,511 ----
return '%s INTEGER PRIMARY KEY' % soClass._idName
! def joinSQLType(self, join):
return 'INT NOT NULL'
***************
*** 522,526 ****
"""
! def queryInsertID(self, table, names, values):
id = self._newID(table)
self._saveDict(table, id, dict(zip(names, values)))
--- 537,541 ----
"""
! def queryInsertID(self, table, idName, names, values):
id = self._newID(table)
self._saveDict(table, id, dict(zip(names, values)))
***************
*** 790,796 ****
results.remove(int(id1))
db[join2 + str(id2)] = ",".join(map(str, results))
-
-
-
class DBMSelectResults(object):
--- 805,808 ----
Index: SQLObject.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/SQLObject.py,v
retrieving revision 1.14
retrieving revision 1.15
diff -C2 -d -r1.14 -r1.15
*** SQLObject.py 7 Apr 2003 01:13:54 -0000 1.14
--- SQLObject.py 7 Apr 2003 19:48:41 -0000 1.15
***************
*** 729,733 ****
# up to DBConnection, since getting a new ID is
# non-standard.
! id = self._connection.queryInsertID(self._table, names, values)
cache = self._connection.cache
cache.created(id, self.__class__, self)
--- 729,734 ----
# up to DBConnection, since getting a new ID is
# non-standard.
! id = self._connection.queryInsertID(self._table, self._idName,
! names, values)
cache = self._connection.cache
cache.created(id, self.__class__, self)
|
|
From: <ian...@us...> - 2003-04-07 19:48:49
|
Update of /cvsroot/sqlobject/SQLObject/tests
In directory sc8-pr-cvs1:/tmp/cvs-serv3612/tests
Modified Files:
SQLObjectTest.py test.py
Log Message:
* Extended the tests to work with Postgres, SQLite, DBM
* Some databases don't implement some features (SQLite doesn't support
dynamic columns, all but MySQL don't support auto-class creation),
at least for now.
* Make SERIAL work for columns under Postgres, as well as _idName
* Fixed LIMIT/OFFSET bugs in Postgres, SQLite
* tableExists in Postgres
Index: SQLObjectTest.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/tests/SQLObjectTest.py,v
retrieving revision 1.4
retrieving revision 1.5
diff -C2 -d -r1.4 -r1.5
*** SQLObjectTest.py 7 Apr 2003 01:13:56 -0000 1.4
--- SQLObjectTest.py 7 Apr 2003 19:48:42 -0000 1.5
***************
*** 2,12 ****
from SQLObject import *
! __connection__ = MySQLConnection(host='localhost',
! db='test',
! user='test',
! passwd='',
! debug=0)
! #__connection__ = DBMConnection('data')
class SQLObjectTest(unittest.TestCase):
--- 2,32 ----
from SQLObject import *
! True, False = 1==1, 0==1
! def mysqlConnection():
! SQLObjectTest.supportDynamic = True
! SQLObjectTest.supportAuto = True
! return MySQLConnection(host='localhost',
! db='test',
! user='test',
! passwd='',
! debug=0)
!
! def dbmConnection():
! SQLObjectTest.supportDynamic = True
! SQLObjectTest.supportAuto = False
! return DBMConnection('data')
!
! def postgresConnection():
! SQLObjectTest.supportDynamic = True
! SQLObjectTest.supportAuto = False
! return PostgresConnection(db='test')
!
! def sqliteConnection():
! SQLObjectTest.supportDynamic = False
! SQLObjectTest.supportAuto = False
! return SQLiteConnection('data/sqlite.data')
!
! supportedDatabases = ['mysql', 'postgres', 'sqlite', 'dbm']
class SQLObjectTest(unittest.TestCase):
***************
*** 22,25 ****
--- 42,47 ----
unittest.TestCase.setUp(self)
#__connection__.debug = self.debugSQL
+ for c in self.classes:
+ c._connection = __connection__
for c in self.classes + [self]:
if hasattr(c, 'drop'):
***************
*** 45,48 ****
__connection__.query(c.drop)
! __all__ = ['SQLObjectTest', '__connection__',]
--- 67,79 ----
__connection__.query(c.drop)
! def setDatabaseType(t):
! global __connection__
! conn = globals()[t + "Connection"]()
! __connection__ = conn
!
! def connection():
! return __connection__
!
! __all__ = ['SQLObjectTest', 'setDatabaseType', 'connection',
! 'supportedDatabases']
Index: test.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/tests/test.py,v
retrieving revision 1.5
retrieving revision 1.6
diff -C2 -d -r1.5 -r1.6
*** test.py 7 Apr 2003 01:13:57 -0000 1.5
--- test.py 7 Apr 2003 19:48:44 -0000 1.6
***************
*** 93,96 ****
--- 93,98 ----
def testDynamicColumn(self):
+ if not self.supportDynamic:
+ return
nickname = StringCol('nickname', length=10)
Person.addColumn(nickname, changeSchema=True)
***************
*** 101,104 ****
--- 103,108 ----
def testDynamicJoin(self):
+ if not self.supportDynamic:
+ return
col = KeyCol('personID', foreignKey='Person')
Phone.addColumn(col, changeSchema=True)
***************
*** 110,115 ****
else:
phone.person = Person.selectBy(name='bob')[0]
! self.assertEqual([p.phone for p in Person.selectBy(name='tim')[0].phones],
! ['555-555-5555', '555-394-2930'])
Phone.delColumn(col, changeSchema=True)
Person.delJoin(join)
--- 114,121 ----
else:
phone.person = Person.selectBy(name='bob')[0]
! l = [p.phone for p in Person.selectBy(name='tim')[0].phones]
! l.sort()
! self.assertEqual(l,
! ['555-394-2930', '555-555-5555'])
Phone.delColumn(col, changeSchema=True)
Person.delJoin(join)
***************
*** 122,126 ****
class AutoTest(SQLObjectTest):
! create = """
CREATE TABLE IF NOT EXISTS auto_test (
id INT AUTO_INCREMENT PRIMARY KEY,
--- 128,132 ----
class AutoTest(SQLObjectTest):
! mysqlCreate = """
CREATE TABLE IF NOT EXISTS auto_test (
id INT AUTO_INCREMENT PRIMARY KEY,
***************
*** 133,143 ****
"""
! drop = """
DROP TABLE IF EXISTS auto_test
"""
def testClassCreate(self):
class AutoTest(SQLObject):
_fromDatabase = True
AutoTest.new(firstName='john',
lastName='doe',
--- 139,167 ----
"""
! postgresCreate = """
! CREATE TABLE auto_test (
! id SERIAL,
! first_name VARCHAR(100),
! last_name VARCHAR(200) NOT NULL,
! age INT,
! created DATETIME NOT NULL,
! happy char(1) DEFAULT 'Y' NOT NULL
! )
! """
!
! mysqlDrop = """
DROP TABLE IF EXISTS auto_test
"""
+ postgresDrop = """
+ DROP TABLE auto_test
+ """
+
def testClassCreate(self):
+ if not self.supportAuto:
+ return
class AutoTest(SQLObject):
_fromDatabase = True
+ _connection = connection()
AutoTest.new(firstName='john',
lastName='doe',
***************
*** 200,205 ****
if __name__ == '__main__':
import unittest, sys
for arg in sys.argv[1:]:
if arg in ('-vv', '--extra-verbose'):
SQLObjectTest.debugSQL = 1
! unittest.main()
--- 224,246 ----
if __name__ == '__main__':
import unittest, sys
+ dbs = []
for arg in sys.argv[1:]:
+ if arg.startswith('-d'):
+ dbs.append(arg[2:])
+ if arg.startswith('--database='):
+ dbs.append(arg[11:])
if arg in ('-vv', '--extra-verbose'):
SQLObjectTest.debugSQL = 1
! sys.argv = [a for a in sys.argv
! if not a.startswith('-d') and not a.startswith('--database=')]
! if not dbs:
! dbs = ['mysql']
! if dbs == ['all']:
! dbs = supportedDatabases
! for db in dbs:
! setDatabaseType(db)
! print 'Testing %s' % db
! try:
! unittest.main()
! except SystemExit:
! pass
|
|
From: <ian...@us...> - 2003-04-07 07:43:40
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv17939/SQLObject
Modified Files:
DBConnection.py
Log Message:
PostgresConnection accepts keyword arguments matching MySQLConnection
(and creates a dsn out of that). DSN is still accepted as before.
Index: DBConnection.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/DBConnection.py,v
retrieving revision 1.12
retrieving revision 1.13
diff -C2 -d -r1.12 -r1.13
*** DBConnection.py 7 Apr 2003 01:13:54 -0000 1.12
--- DBConnection.py 7 Apr 2003 07:43:36 -0000 1.13
***************
*** 404,412 ****
class PostgresConnection(DBAPI):
! def __init__(self, dsn, autoCommit=1, **kw):
assert psycopg, 'psycopg module cannot be found'
if not autoCommit and not kw.has_key('pool'):
# Pooling doesn't work with transactions...
kw['pool'] = 0
self.dsn = dsn
DBAPI.__init__(self, **kw)
--- 404,425 ----
class PostgresConnection(DBAPI):
! def __init__(self, dsn=None, host=None, db=None,
! user=None, passwd=None, autoCommit=1, **kw):
assert psycopg, 'psycopg module cannot be found'
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)
|
|
From: <ian...@us...> - 2003-04-07 01:14:29
|
Update of /cvsroot/sqlobject/SQLObject In directory sc8-pr-cvs1:/tmp/cvs-serv1207 Modified Files: .cvsignore Log Message: * Moved all SQL into DBConnection (_SO_* methods) * A test case for RelatedJoin * Created a DBM-based backend. Passes almost all tests! * Incomplete Pickle-based backend (like DBM, only records are kept in individual files... I'm not sure I want to use it, though). Index: .cvsignore =================================================================== RCS file: /cvsroot/sqlobject/SQLObject/.cvsignore,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -C2 -d -r1.1.1.1 -r1.2 *** .cvsignore 26 Feb 2003 23:59:40 -0000 1.1.1.1 --- .cvsignore 7 Apr 2003 01:13:52 -0000 1.2 *************** *** 3,4 **** --- 3,5 ---- dist MANIFEST + data |
|
From: <ian...@us...> - 2003-04-07 01:14:02
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv1207/SQLObject
Modified Files:
DBConnection.py SQLBuilder.py SQLObject.py
Log Message:
* Moved all SQL into DBConnection (_SO_* methods)
* A test case for RelatedJoin
* Created a DBM-based backend. Passes almost all tests!
* Incomplete Pickle-based backend (like DBM, only records are kept
in individual files... I'm not sure I want to use it, though).
Index: DBConnection.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/DBConnection.py,v
retrieving revision 1.11
retrieving revision 1.12
diff -C2 -d -r1.11 -r1.12
*** DBConnection.py 6 Apr 2003 07:56:27 -0000 1.11
--- DBConnection.py 7 Apr 2003 01:13:54 -0000 1.12
***************
*** 5,8 ****
--- 5,15 ----
from Cache import CacheSet
import Col
+ import fcntl
+ try:
+ import cPickle as pickle
+ except ImportError:
+ import pickle
+ import os
+ import anydbm
try:
***************
*** 20,26 ****
except ImportError:
sqlite = None
! __all__ = ['MySQLConnection', 'PostgresConnection', 'SQLiteConnection']
_connections = {}
--- 27,35 ----
except ImportError:
sqlite = None
+ import re
! __all__ = ['MySQLConnection', 'PostgresConnection', 'SQLiteConnection',
! 'PickleConnection', 'DBMConnection']
_connections = {}
***************
*** 290,293 ****
--- 299,310 ----
SQLBuilder.sqlRepr(secondValue)))
+ def _SO_columnClause(self, soClass, kw):
+ return ' '.join(['%s = %s' %
+ (soClass._SO_columnDict[key].dbName,
+ SQLBuilder.sqlRepr(value))
+ for key, value
+ in kw.items()])
+
+
class Transaction(object):
***************
*** 469,470 ****
--- 486,855 ----
# turn it into a boolean:
return not not result
+
+ ########################################
+ ## 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, names, values):
+ 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), match.group(1))
+
+ 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(SQLBuilder.SmartTable(soClass._table), name) == value)
+ return SQLBuilder.AND(*clauses)
+
+ def _SO_selectJoin(self, soClass, column, value):
+ results = []
+ for id in self._allIDs(soClass._table):
+ d = self._fetchDict(soClass._table, id)
+ if d[column] == value:
+ results.append((id,))
+ return results
+
+ class PickleConnection(FileConnection):
+
+ def __init__(self, path, **kw):
+ self.path = path
+ FileConnection.__init__(self, **kw)
+
+ def _filename(self, table, id):
+ return '%s-%i.pickle' % (table, id)
+
+ def _newID(self, table):
+ idFilename = os.path.join(self.path, "%s-ids.txt" % table)
+ try:
+ f = open(idFilename, "r+")
+ except IOError:
+ f = open(idFilename, "w")
+ f.write("0")
+ f.close()
+ f = open(idFilename, "r+")
+ fcntl.lockf(f, fcntl.LOCK_EX)
+ id = int(f.read()) + 1
+ # @@: Can I just close this and open it in write mode, while
+ # retaining this lock?
+ f.seek(0)
+ f.write(str(id))
+ fcntl.lockf(f, fcntl.LOCK_UN)
+ f.close()
+ return id
+
+ def _fetchDict(self, table, id):
+ f = open(self._filename(table, id), 'rb')
+ d = pickle.load(f)
+ f.close()
+ return d
+
+ def _saveDict(self, table, id, d):
+ f = open(self._filename(table, id), 'wb')
+ pickle.dump(d, f)
+ f.close()
+
+ def _fetchTables(self):
+ filename = os.path.join(self.path, "table-list.txt")
+ try:
+ f = open(filename, "r")
+ except IOError:
+ return []
+ lines = [l.strip() for l in f.readlines()]
+ f.close()
+ return lines
+
+ def _saveTables(self, tables):
+ filename = os.path.join(self.path, "table-list.txt")
+ f = open(filename, "w")
+ f.write('\n'.join(tables))
+ f.close()
+
+ def tableExists(self, table):
+ return table in self._fetchTables()
+
+ def createTable(self, soClass):
+ tables = self._fetchTables()
+ tables.append(soClass._table)
+ self._saveTables(tables)
+
+ def dropTable(self, tableName):
+ tables = self._fetchTables()
+ tables.remove(tableName)
+ self._saveTables(tables)
+ self.clearTable(tableName)
+
+ def clearTable(self, tableName):
+ for filename in os.listdir(self.path):
+ if filename.startswith(tableName + "-"):
+ os.unlink(os.path.join(self.path, filename))
+
+ def _SO_delete(self, so):
+ os.unlink(self._filename(so._table, so.id))
+
+
+ class DBMConnection(FileConnection):
+
+ def __init__(self, path, **kw):
+ 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 = {}
+ 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 _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):
+ 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()
+ def cmper(a, b, orderBy=select.ops['orderBy']):
+ return cmp(getattr(a, orderBy),
+ getattr(b, orderBy))
+ results.sort(cmper)
+ 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(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
Index: SQLBuilder.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/SQLBuilder.py,v
retrieving revision 1.2
retrieving revision 1.3
diff -C2 -d -r1.2 -r1.3
*** SQLBuilder.py 1 Apr 2003 16:51:28 -0000 1.2
--- SQLBuilder.py 7 Apr 2003 01:13:54 -0000 1.3
***************
*** 11,15 ****
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
! License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
--- 11,15 ----
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
! License, or (at your option any later version.
This program is distributed in the hope that it will be useful,
***************
*** 94,98 ****
origISOStr = None
DateTimeType = None
! import re
def isoStr(val):
--- 94,99 ----
origISOStr = None
DateTimeType = None
! import re, fnmatch
! import operator
def isoStr(val):
***************
*** 131,134 ****
--- 132,141 ----
+ def execute(expr, executor):
+ if hasattr(expr, 'execute'):
+ return expr.execute(executor)
+ else:
+ return expr
+
########################################
## Expression generation
***************
*** 230,236 ****
return {}
class SQLOp(SQLExpression):
def __init__(self, op, expr1, expr2):
! self.op = op
self.expr1 = expr1
self.expr2 = expr2
--- 237,257 ----
return {}
+ operatorMap = {
+ "+": operator.add,
+ "/": operator.div,
+ "-": operator.sub,
+ "*": operator.mul,
+ "<": operator.lt,
+ "<=": operator.le,
+ "=": operator.eq,
+ "!=": operator.ne,
+ ">=": operator.ge,
+ ">": operator.gt,
+ "IN": operator.contains,
+ }
+
class SQLOp(SQLExpression):
def __init__(self, op, expr1, expr2):
! self.op = op.upper()
self.expr1 = expr1
self.expr2 = expr2
***************
*** 239,242 ****
--- 260,284 ----
def components(self):
return [self.expr1, self.expr2]
+ def execute(self, executor):
+ if self.op == "AND":
+ return execute(self.expr1, executor) \
+ and execute(self.expr2, executor)
+ elif self.op == "OR":
+ return execute(self.expr1, executor) \
+ or execute(self.expr2, executor)
+ elif self.op == "LIKE":
+ if not hasattr(self, '_regex'):
+ # @@: Crude, not entirely accurate
+ dest = self.expr2
+ dest = dest.replace("%%", "\001")
+ dest = dest.replace("*", "\002")
+ dest = dest.replace("%", "*")
+ dest = dest.replace("\001", "%")
+ dest = dest.replace("\002", "[*]")
+ self._regex = re.compile(fnmatch.translate(dest), re.I)
+ return self._regex.search(execute(self.expr1, executor))
+ else:
+ return operatorMap[self.op.upper()](execute(self.expr1, executor),
+ execute(self.expr2, executor))
class SQLCall(SQLExpression):
***************
*** 248,251 ****
--- 290,295 ----
def components(self):
return [self.expr] + list(self.args)
+ def execute(self, executor):
+ raise ValueError, "I don't yet know how to locally execute functions"
class SQLPrefix(SQLExpression):
***************
*** 257,260 ****
--- 301,312 ----
def components(self):
return [self.expr]
+ def execute(self, executor):
+ expr = execute(self.expr, executor)
+ if prefix == "+":
+ return expr
+ elif prefix == "-":
+ return -expr
+ elif prefix.upper() == "NOT":
+ return not expr
class SQLConstant(SQLExpression):
***************
*** 263,267 ****
--- 315,328 ----
def sqlRepr(self):
return self.const
+ def execute(self, executor):
+ raise ValueError, "I don't yet know how to execute SQL constants"
+
+ class SQLTrueClauseClass(SQLExpression):
+ def sqlRepr(self):
+ return "1 = 1"
+ def execute(self, executor):
+ return 1
+ SQLTrueClause = SQLTrueClauseClass()
########################################
***************
*** 280,283 ****
--- 341,346 ----
def sqlRepr(self):
return str(self.tableName)
+ def execute(self, executor):
+ raise ValueError, "Tables don't have values"
class SmartTable(Table):
***************
*** 285,289 ****
def __getattr__(self, attr):
if self._capRE.search(attr):
! attr = attr[0] + self._capRE.sub(lambda m: '_%s' % m.group(0), attr[1:])
return Table.__getattr__(self, attr)
--- 348,352 ----
def __getattr__(self, attr):
if self._capRE.search(attr):
! attr = attr[0] + self._capRE.sub(lambda m: '_%s' % m.group(0).lower(), attr[1:])
return Table.__getattr__(self, attr)
***************
*** 296,299 ****
--- 359,364 ----
def tablesUsedImmediate(self):
return [self.tableName]
+ def execute(self, executor):
+ return executor.field(self.tableName, self.fieldName)
class ConstantSpace:
Index: SQLObject.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/SQLObject.py,v
retrieving revision 1.13
retrieving revision 1.14
diff -C2 -d -r1.13 -r1.14
*** SQLObject.py 6 Apr 2003 07:56:27 -0000 1.13
--- SQLObject.py 7 Apr 2003 01:13:54 -0000 1.14
***************
*** 576,580 ****
self._connection._SO_update(self,
[(self._SO_columnDict[name].dbName,
! SQLBuilder.sqlRepr(value))])
# _SO_autoInitDone implies there's a cached value we also
--- 576,580 ----
self._connection._SO_update(self,
[(self._SO_columnDict[name].dbName,
! value)])
# _SO_autoInitDone implies there's a cached value we also
***************
*** 763,771 ****
def selectBy(cls, **kw):
return SelectResults(cls,
! ' '.join(['%s = %s' %
! (cls._SO_columnDict[key].dbName,
! SQLBuilder.sqlRepr(value))
! for key, value
! in kw.items()]))
selectBy = classmethod(selectBy)
--- 763,767 ----
def selectBy(cls, **kw):
return SelectResults(cls,
! cls._connection._SO_columnClause(cls, kw))
selectBy = classmethod(selectBy)
***************
*** 987,991 ****
self.sourceClass = sourceClass
if isinstance(clause, str) and clause == 'all':
! clause = SQLBuilder.SQLConstant('1 = 1')
self.clause = clause
tablesDict = SQLBuilder.tablesUsedDict(self.clause)
--- 983,987 ----
self.sourceClass = sourceClass
if isinstance(clause, str) and clause == 'all':
! clause = SQLBuilder.SQLTrueClause
self.clause = clause
tablesDict = SQLBuilder.tablesUsedDict(self.clause)
|
|
From: <ian...@us...> - 2003-04-07 01:14:02
|
Update of /cvsroot/sqlobject/SQLObject/tests
In directory sc8-pr-cvs1:/tmp/cvs-serv1207/tests
Modified Files:
SQLObjectTest.py test.py
Log Message:
* Moved all SQL into DBConnection (_SO_* methods)
* A test case for RelatedJoin
* Created a DBM-based backend. Passes almost all tests!
* Incomplete Pickle-based backend (like DBM, only records are kept
in individual files... I'm not sure I want to use it, though).
Index: SQLObjectTest.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/tests/SQLObjectTest.py,v
retrieving revision 1.3
retrieving revision 1.4
diff -C2 -d -r1.3 -r1.4
*** SQLObjectTest.py 31 Mar 2003 05:51:28 -0000 1.3
--- SQLObjectTest.py 7 Apr 2003 01:13:56 -0000 1.4
***************
*** 8,11 ****
--- 8,13 ----
debug=0)
+ #__connection__ = DBMConnection('data')
+
class SQLObjectTest(unittest.TestCase):
Index: test.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/tests/test.py,v
retrieving revision 1.4
retrieving revision 1.5
diff -C2 -d -r1.4 -r1.5
*** test.py 6 Apr 2003 07:56:06 -0000 1.4
--- test.py 7 Apr 2003 01:13:57 -0000 1.5
***************
*** 9,25 ****
class Counter(SQLObject):
- create = """
- CREATE TABLE IF NOT EXISTS counter (
- id INT PRIMARY KEY AUTO_INCREMENT,
- number INT NOT NULL
- )
- """
-
- drop = """
- DROP TABLE IF EXISTS counter
- """
-
_columns = [
! Col('number'),
]
--- 9,14 ----
class Counter(SQLObject):
_columns = [
! IntCol('number', notNull=True),
]
***************
*** 57,74 ****
class Counter2(SQLObject):
- create = """
- CREATE TABLE IF NOT EXISTS counter2 (
- id INT PRIMARY KEY AUTO_INCREMENT,
- n1 INT NOT NULL,
- n2 INT NOT NULL
- )
- """
-
- drop = """
- DROP TABLE IF EXISTS counter2
- """
-
_columns = [
! 'n1', 'n2',
]
--- 46,52 ----
class Counter2(SQLObject):
_columns = [
! IntCol('n1', notNull=True),
! IntCol('n2', notNull=True),
]
|
|
From: <ian...@us...> - 2003-04-06 07:56:29
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv16872/SQLObject
Modified Files:
DBConnection.py SQLObject.py
Log Message:
Moved all SQL to DBConnection
Index: DBConnection.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/DBConnection.py,v
retrieving revision 1.10
retrieving revision 1.11
diff -C2 -d -r1.10 -r1.11
*** DBConnection.py 2 Apr 2003 18:19:26 -0000 1.10
--- DBConnection.py 6 Apr 2003 07:56:27 -0000 1.11
***************
*** 190,211 ****
return q
! def createJoinSQL(self, soClass):
! result = []
! for join in soClass._joins:
! if not join.hasIntermediateTable():
! continue
! # This join will show up twice, in each of the
! # classes, but we only create the table once. We
! # arbitrarily create it while we're creating the
! # alphabetically earlier class.
! if join.callingClass > join.otherClass:
! continue
! result.append('CREATE TABLE %s (\n%s %s,\n%s %s\n)'
! % (join.intermediateTable,
! join.joinColumn,
! self.joinSQLType(join),
! join.otherColumn,
! self.joinSQLType(join)))
! return result
def createTable(self, soClass):
--- 190,203 ----
return q
! def _SO_createJoinTable(self, join):
! self.query('CREATE TABLE %s (\n%s %s,\n%s %s\n)' %
! (join.intermediateTable,
! join.joinColumn,
! self.joinSQLType(join),
! join.otherColumn,
! self.joinSQLType(join)))
!
! def _SO_dropJoinTable(self, join):
! self.query("DROP TABLE %s" % join.intermediateTable)
def createTable(self, soClass):
***************
*** 231,234 ****
--- 223,292 ----
# that.
self.query("DELETE FROM %s" % tableName)
+
+ # The _SO_* series of methods are sorts of "friend" methods
+ # with SQLObject. They grab values from the SQLObject instances
+ # or classes freely, but keep the SQLObject class from accessing
+ # the database directly. This way no SQL is actually created
+ # in SQLObject.
+
+ def _SO_update(self, so, values):
+ self.query("UPDATE %s SET %s WHERE %s = %s" %
+ (so._table,
+ ", ".join(["%s = %s" % (dbName, SQLBuilder.sqlRepr(value))
+ for dbName, value in values]),
+ so._idName,
+ SQLBuilder.sqlRepr(so.id)))
+
+ def _SO_selectOne(self, so, columnNames):
+ return self.queryOne("SELECT %s FROM %s WHERE %s = %s" %
+ (", ".join(columnNames),
+ so._table,
+ so._idName,
+ SQLBuilder.sqlRepr(so.id)))
+
+ def _SO_selectOneAlt(self, cls, columnNames, column, value):
+ return self.queryOne("SELECT %s FROM %s WHERE %s = %s" %
+ (", ".join(columnNames),
+ cls._table,
+ column,
+ SQLBuilder.sqlRepr(value)))
+
+ def _SO_delete(self, so):
+ self.query("DELETE FROM %s WHERE %s = %s" %
+ (so._table,
+ so._idName,
+ SQLBuilder.sqlRepr(so.id)))
+
+ def _SO_selectJoin(self, soClass, column, value):
+ return self.queryAll("SELECT %s FROM %s WHERE %s = %s" %
+ (soClass._idName,
+ soClass._table,
+ column,
+ SQLBuilder.sqlRepr(value)))
+
+ def _SO_intermediateJoin(self, table, getColumn, joinColumn, value):
+ return self.queryAll("SELECT %s FROM %s WHERE %s = %s" %
+ (getColumn,
+ table,
+ joinColumn,
+ SQLBuilder.sqlRepr(value)))
+
+ def _SO_intermediateDelete(self, table, firstColumn, firstValue,
+ secondColumn, secondValue):
+ self.query("DELETE FROM %s WHERE %s = %s AND %s = %s" %
+ (table,
+ firstColumn,
+ SQLBuilder.sqlRepr(firstValue),
+ secondColumn,
+ SQLBuilder.sqlRepr(secondValue)))
+
+ def _SO_intermediateInsert(self, table, firstColumn, firstValue,
+ secondColumn, secondValue):
+ self.query("INSERT INTO %s (%s, %s) VALUES (%s, %s)" %
+ (table,
+ firstColumn,
+ secondColumn,
+ SQLBuilder.sqlRepr(firstValue),
+ SQLBuilder.sqlRepr(secondValue)))
class Transaction(object):
Index: SQLObject.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/SQLObject.py,v
retrieving revision 1.12
retrieving revision 1.13
diff -C2 -d -r1.12 -r1.13
*** SQLObject.py 31 Mar 2003 05:51:28 -0000 1.12
--- SQLObject.py 6 Apr 2003 07:56:27 -0000 1.13
***************
*** 505,509 ****
# And again...
! func = eval('lambda self, obj: self._SO_joinList[%i].add(self, obj)' % (len(dict['_SO_joinList'])-1))
setattr(cls, '_SO_add' + join.addRemovePrefix, func)
if not hasattr(cls, 'add' + appendMeth):
--- 505,509 ----
# And again...
! func = eval('lambda self, obj: self._SO_joinList[%i].add(self, obj)' % (len(cls._SO_joinList)-1))
setattr(cls, '_SO_add' + join.addRemovePrefix, func)
if not hasattr(cls, 'add' + appendMeth):
***************
*** 574,579 ****
# There's a write lock. Not sure if I need this...
self._SO_writeLock.acquire()
! self._connection.query("UPDATE %s SET %s = %s WHERE %s = %s" % (self._table, self._SO_columnDict[name].dbName, SQLBuilder.sqlRepr(value), self._idName, SQLBuilder.sqlRepr(self.id)))
!
# _SO_autoInitDone implies there's a cached value we also
# have to update.
--- 574,581 ----
# There's a write lock. Not sure if I need this...
self._SO_writeLock.acquire()
! self._connection._SO_update(self,
! [(self._SO_columnDict[name].dbName,
! SQLBuilder.sqlRepr(value))])
!
# _SO_autoInitDone implies there's a cached value we also
# have to update.
***************
*** 610,618 ****
setattr(self, name, value)
! # Here we construct the UPDATE, simple...
! q = "UPDATE %s SET " % (self._table)
! sets = ', '.join(["%s = %s" % (self._SO_columnDict[name].dbName, SQLBuilder.sqlRepr(value)) for name, value in toUpdate.items()])
! clause = " WHERE %s = %s" % (self._idName, self.id)
! self._connection.query(q + sets + clause)
self._SO_writeLock.release()
--- 612,616 ----
setattr(self, name, value)
! self._connection._SO_update(self, [(self._SO_columnDict[name].dbName, value) for name, value in toUpdate.items()])
self._SO_writeLock.release()
***************
*** 632,639 ****
# Database (_-using) names:
dbNames = [col.dbName for col in self._columns]
!
! q = "SELECT %s FROM %s WHERE %s = %s" % \
! (', '.join(dbNames), self._table, self._idName, self.id)
! results = self._connection.queryOne(q)
assert results, "The object %s by the ID %s does not exist" % (self.__class__.__name__, self.id)
--- 630,635 ----
# Database (_-using) names:
dbNames = [col.dbName for col in self._columns]
!
! results = self._connection._SO_selectOne(self, dbNames)
assert results, "The object %s by the ID %s does not exist" % (self.__class__.__name__, self.id)
***************
*** 654,661 ****
assert not self._SO_obsolete, "%s with id %s has become obsolete" \
% (self.__class__.__name__, self.id)
! q = "SELECT %s FROM %s WHERE %s = %s" % \
! (self._SO_columnDict[name].name, self._table, self._idName, self.id)
self._SO_writeLock.acquire()
! results = self._connection.queryOne(q)
self._SO_writeLock.release()
return results[0]
--- 650,656 ----
assert not self._SO_obsolete, "%s with id %s has become obsolete" \
% (self.__class__.__name__, self.id)
! # @@: do we really need this lock?
self._SO_writeLock.acquire()
! results = self._connection._SO_selectOne(self, [self._SO_columnDict[name].dbName])
self._SO_writeLock.release()
return results[0]
***************
*** 743,753 ****
def _SO_fetchAlternateID(cls, dbIDName, value):
! q = "SELECT %s, %s FROM %s WHERE %s = %s" % \
! (cls._idName,
! ", ".join([col.dbName for col in cls._columns]),
! cls._table,
! dbIDName,
! SQLBuilder.sqlRepr(value))
! result = cls._connection.queryOne(q)
obj = cls(result[0])
if not obj._SO_autoInitDone:
--- 738,747 ----
def _SO_fetchAlternateID(cls, dbIDName, value):
! result = cls._connection._SO_selectOneAlt(
! cls,
! [cls._idName] +
! [col.dbName for col in cls._columns],
! dbIDName,
! value)
obj = cls(result[0])
if not obj._SO_autoInitDone:
***************
*** 778,785 ****
# 3-03 @@: Should these have a connection argument?
! def dropTable(cls, ifExists=False):
if ifExists and not cls._connection.tableExists(cls._table):
return
cls._connection.dropTable(cls._table)
dropTable = classmethod(dropTable)
--- 772,781 ----
# 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)
dropTable = classmethod(dropTable)
***************
*** 793,799 ****
def createJoinTables(cls, ifExists=False):
- # 3-03: This should be calling _connection.createTable,
- # but right now it's making tables directly.
- result = []
for join in cls._joins:
if not join.hasIntermediateTable():
--- 789,792 ----
***************
*** 808,820 ****
cls._connection.tableExists(join.intermediateTable):
continue
! self._connection.query(
! 'CREATE TABLE %s (\n%s %s,\n%s %s\n)'
! % (join.intermediateTable,
! join.joinColumn,
! self.joinSQLType(join),
! join.otherColumn,
! self.joinSQLType(join)))
createJoinTables = classmethod(createJoinTables)
def clearTable(cls):
# 3-03 @@: Maybe this should check the cache... but it's
--- 801,821 ----
cls._connection.tableExists(join.intermediateTable):
continue
! cls._connection._SO_createJoinTable(join)
!
createJoinTables = classmethod(createJoinTables)
+ def dropJoinTables(cls, ifExists=False):
+ for join in cls._joins:
+ if not join.hasIntermediateTable():
+ continue
+ if join.callingClass > join.otherClass:
+ continue
+ if ifExists and \
+ not cls._connection.tableExists(join.intermediateTable):
+ continue
+ cls._connection._SO_dropJoinTable(join)
+
+ dropJoinTables = classmethod(dropJoinTables)
+
def clearTable(cls):
# 3-03 @@: Maybe this should check the cache... but it's
***************
*** 827,834 ****
self._SO_obsolete = True
self._SO_autoInitDone = False
! self._connection.query("""
! DELETE FROM %s
! WHERE %s = %s
! """ % (self._table, self._idName, SQLBuilder.sqlRepr(self.id)))
self._connection.cache.purge(self.id)
--- 828,834 ----
self._SO_obsolete = True
self._SO_autoInitDone = False
! # huh?
! #self._SO_delete(self)
! self._connection._SO_delete(self)
self._connection.cache.purge(self.id)
***************
*** 923,932 ****
# so we have to fetch the actual class definition:
cls = findClass(self.otherClass)
! ids = inst._connection.queryAll(
! """SELECT %s FROM %s
! WHERE %s = %s""" %
! (cls._idName, cls._table,
! self.joinColumn,
! SQLBuilder.sqlRepr(inst.id)))
return [cls(id) for (id,) in ids]
--- 923,930 ----
# so we have to fetch the actual class definition:
cls = findClass(self.otherClass)
! ids = inst._connection._SO_selectJoin(
! cls,
! self.joinColumn,
! inst.id)
return [cls(id) for (id,) in ids]
***************
*** 958,992 ****
cls = findClass(self.otherClass)
me = findClass(self.callingClass)
! ids = me._connection.queryAll(
! """SELECT %s FROM %s
! WHERE %s = %s""" %
! (self.otherColumn,
! self.intermediateTable,
! self.joinColumn,
! SQLBuilder.sqlRepr(inst.id)))
return [cls(id) for (id,) in ids]
def remove(self, inst, other):
me = findClass(self.callingClass)
! me._connection.query(
! """DELETE FROM %s
! WHERE %s = %s
! AND %s = %s""" %
! (self.intermediateTable,
! self.joinColumn,
! getID(inst.id),
! self.otherColumn,
! getID(other)))
def add(self, inst, other):
me = findClass(self.callingClass)
! me._connection.query(
! """INSERT INTO %s (%s, %s)
! VALUES (%s, %s)""" %
! (self.intermediateTable,
! self.joinColumn,
! self.otherColumn,
! getID(inst),
! getID(other)))
class SelectResults(object):
--- 956,983 ----
cls = findClass(self.otherClass)
me = findClass(self.callingClass)
! ids = me._connection._SO_intermediateJoin(
! self.intermediateTable,
! self.otherColumn,
! self.joinColumn,
! inst.id)
return [cls(id) for (id,) in ids]
def remove(self, inst, other):
me = findClass(self.callingClass)
! me._connection._SO_intermediateDelete(
! self.intermediateTable,
! self.joinColumn,
! getID(inst),
! self.otherColumn,
! getID(other))
def add(self, inst, other):
me = findClass(self.callingClass)
! me._connection._SO_intermediateInsert(
! self.intermediateTable,
! self.joinColumn,
! getID(inst),
! self.otherColumn,
! getID(other))
class SelectResults(object):
|