sqlobject-cvs Mailing List for SQLObject (Page 175)
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
|
Sep
|
Oct
|
Nov
|
Dec
|
From: <sub...@co...> - 2004-12-02 19:57:11
|
Author: phd Date: 2004-12-02 19:57:06 +0000 (Thu, 02 Dec 2004) New Revision: 431 Modified: home/phd/SQLObject/inheritance/docs/SQLObject.txt home/phd/SQLObject/inheritance/examples/personaddress.py Log: Merged changes from trunk revisions 425:430. Modified: home/phd/SQLObject/inheritance/docs/SQLObject.txt =================================================================== --- home/phd/SQLObject/inheritance/docs/SQLObject.txt 2004-12-02 16:46:20 UTC (rev 430) +++ home/phd/SQLObject/inheritance/docs/SQLObject.txt 2004-12-02 19:57:06 UTC (rev 431) @@ -478,7 +478,8 @@ method, which is called after an object is fetched or inserted. This method has the signature ``_init(self, id, connection=None, selectResults=None)``, though you may just want to use ``_init(self, -*args, **kw)``. +*args, **kw)``. **Note:** don't forget to call +``SQLObject._init(self, *args, **kw)`` if you override the method! Adding Magic Attributes (properties) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Modified: home/phd/SQLObject/inheritance/examples/personaddress.py =================================================================== --- home/phd/SQLObject/inheritance/examples/personaddress.py 2004-12-02 16:46:20 UTC (rev 430) +++ home/phd/SQLObject/inheritance/examples/personaddress.py 2004-12-02 19:57:06 UTC (rev 431) @@ -61,9 +61,9 @@ AND(Address.q.personID == Person.q.id, Address.q.zip.startswith('504'))) print list(peeps) -# SELECT person.id FROM person, phone_number -# WHERE (phone_number.id = person.id AND -# phone_number.phone_number LIKE '612%'); +# SELECT person.id FROM person, address +# WHERE (address.person_id = person.id AND +# address.zip LIKE '612%'); ## end snippet ## Snippet "person-select3" |
From: <sub...@co...> - 2004-12-02 16:46:25
|
Author: ianb Date: 2004-12-02 16:46:20 +0000 (Thu, 02 Dec 2004) New Revision: 430 Modified: trunk/SQLObject/docs/SQLObject.txt Log: Noted that SQLObject._init must be called, per Carlos Ribeiro's suggestion. Modified: trunk/SQLObject/docs/SQLObject.txt =================================================================== --- trunk/SQLObject/docs/SQLObject.txt 2004-12-01 16:47:40 UTC (rev 429) +++ trunk/SQLObject/docs/SQLObject.txt 2004-12-02 16:46:20 UTC (rev 430) @@ -478,7 +478,8 @@ method, which is called after an object is fetched or inserted. This method has the signature ``_init(self, id, connection=None, selectResults=None)``, though you may just want to use ``_init(self, -*args, **kw)``. +*args, **kw)``. **Note:** don't forget to call +``SQLObject._init(self, *args, **kw)`` if you override the method! Adding Magic Attributes (properties) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
From: <sub...@co...> - 2004-12-01 16:47:43
|
Author: ianb Date: 2004-12-01 16:47:40 +0000 (Wed, 01 Dec 2004) New Revision: 429 Modified: trunk/SQLObject/examples/personaddress.py Log: Typo in a comment (propagated to the docs) Modified: trunk/SQLObject/examples/personaddress.py =================================================================== --- trunk/SQLObject/examples/personaddress.py 2004-12-01 14:25:00 UTC (rev 428) +++ trunk/SQLObject/examples/personaddress.py 2004-12-01 16:47:40 UTC (rev 429) @@ -61,9 +61,9 @@ AND(Address.q.personID == Person.q.id, Address.q.zip.startswith('504'))) print list(peeps) -# SELECT person.id FROM person, phone_number -# WHERE (phone_number.id = person.id AND -# phone_number.phone_number LIKE '612%'); +# SELECT person.id FROM person, address +# WHERE (address.person_id = person.id AND +# address.zip LIKE '612%'); ## end snippet ## Snippet "person-select3" |
From: <sub...@co...> - 2004-12-01 14:25:05
|
Author: phd Date: 2004-12-01 14:25:00 +0000 (Wed, 01 Dec 2004) New Revision: 428 Modified: home/phd/SQLObject/inheritance/docs/Inheritance.txt Log: Corrected a number of misspellings and grammar errors. Modified: home/phd/SQLObject/inheritance/docs/Inheritance.txt =================================================================== --- home/phd/SQLObject/inheritance/docs/Inheritance.txt 2004-12-01 13:06:25 UTC (rev 427) +++ home/phd/SQLObject/inheritance/docs/Inheritance.txt 2004-12-01 14:25:00 UTC (rev 428) @@ -106,20 +106,19 @@ 'inherited' classes. These clauses are the link between the id and the parent id. This will look like the following request:: - SELECT employee.id, employee.id, employee.first_name, - employee.last_name, from employee FROM person, employee WHERE - person.first_name = 'Jane' AND employee.position = 'Chief' AND person.id - = employee.id + SELECT employee.id, employee.first_name, employee.last_name + FROM person, employee WHERE person.first_name = 'Jane' + AND employee.position = 'Chief' AND person.id = employee.id Some limitation or notice about this version: -* Only simple inheritance will work. It is not possible to inherits +* Only simple inheritance will work. It is not possible to inherit from multiple SQLObject classes. -* It is possible to inherits from an inherited class and this will - works well. In the above exemple, you can have a Chief class that +* It is possible to inherit from an inherited class and this will + work well. In the above exemple, you can have a Chief class that inherits from Employee and all parents attributes will be available through the Chief class. -* You may not redefine a parent column in a inherited class (this +* You may not redefine columns in an inherited class (this will raise an exception). * If you don't want 'childName' columns in your last class (one that will never be inherited), you must set '_inheritable' to 0 in this |
From: <sub...@co...> - 2004-12-01 13:06:27
|
Author: phd Date: 2004-12-01 13:06:25 +0000 (Wed, 01 Dec 2004) New Revision: 427 Modified: home/phd/SQLObject/inheritance/docs/Inheritance.txt Log: Fixed a misspelling. Modified: home/phd/SQLObject/inheritance/docs/Inheritance.txt =================================================================== --- home/phd/SQLObject/inheritance/docs/Inheritance.txt 2004-12-01 13:03:37 UTC (rev 426) +++ home/phd/SQLObject/inheritance/docs/Inheritance.txt 2004-12-01 13:06:25 UTC (rev 427) @@ -40,7 +40,7 @@ A new class attribute ``_inheritable`` is added. When this new attribute is set to 1, the class is marked 'inheritable' and a new -colomns will automatically be added: childName (TEXT). +columns will automatically be added: childName (TEXT). Each class that inherits from a parent class will get the same ID as the parent class. So, there is no need to keep track of parent ID and |
From: <sub...@co...> - 2004-12-01 13:03:39
|
Author: phd Date: 2004-12-01 13:03:37 +0000 (Wed, 01 Dec 2004) New Revision: 426 Modified: home/phd/SQLObject/inheritance/docs/Inheritance.txt Log: Corrected plural form, changed to single (classess => class, have => has). Modified: home/phd/SQLObject/inheritance/docs/Inheritance.txt =================================================================== --- home/phd/SQLObject/inheritance/docs/Inheritance.txt 2004-12-01 12:31:13 UTC (rev 425) +++ home/phd/SQLObject/inheritance/docs/Inheritance.txt 2004-12-01 13:03:37 UTC (rev 426) @@ -3,7 +3,7 @@ Inheritance ----------- -* As suggested by Ian Bicking, each child classes now have the same +* As suggested by Ian Bicking, each child class now has the same ID than the parent class. No more need for childID column and parent foreignKey (with a small speed boost). * No more need to call getSubClass as the 'latest' child will always |
From: <sub...@co...> - 2004-12-01 12:31:17
|
Author: phd Date: 2004-12-01 12:31:13 +0000 (Wed, 01 Dec 2004) New Revision: 425 Modified: home/phd/SQLObject/inheritance/docs/Authors.txt home/phd/SQLObject/inheritance/docs/Inheritance.txt Log: Chnaged my address to be the real email; I am not afarid of spam, and I don't want to put a burden of editing the address on users. Fixed a minor problem (indentation) in Inheritance.txt. Modified: home/phd/SQLObject/inheritance/docs/Authors.txt =================================================================== --- home/phd/SQLObject/inheritance/docs/Authors.txt 2004-11-30 13:08:08 UTC (rev 424) +++ home/phd/SQLObject/inheritance/docs/Authors.txt 2004-12-01 12:31:13 UTC (rev 425) @@ -16,4 +16,4 @@ * Daniel Savard, Xsoli Inc <sqlobject at xsoli.com> * alexander smishlajev <alex at ank-sia.com> * Yaroslav Samchuk <yarcat at ank-sia.com> -* Oleg Broytmann <phd at phd.pp.ru> +* Oleg Broytmann <ph...@ph...> Modified: home/phd/SQLObject/inheritance/docs/Inheritance.txt =================================================================== --- home/phd/SQLObject/inheritance/docs/Inheritance.txt 2004-11-30 13:08:08 UTC (rev 424) +++ home/phd/SQLObject/inheritance/docs/Inheritance.txt 2004-12-01 12:31:13 UTC (rev 425) @@ -107,9 +107,9 @@ parent id. This will look like the following request:: SELECT employee.id, employee.id, employee.first_name, -employee.last_name, from employee FROM person, employee WHERE -person.first_name = 'Jane' AND employee.position = 'Chief' AND person.id -= employee.id + employee.last_name, from employee FROM person, employee WHERE + person.first_name = 'Jane' AND employee.position = 'Chief' AND person.id + = employee.id Some limitation or notice about this version: |
From: <sub...@co...> - 2004-11-30 13:08:11
|
Author: phd Date: 2004-11-30 13:08:08 +0000 (Tue, 30 Nov 2004) New Revision: 424 Modified: home/phd/SQLObject/inheritance/sqlobject/main.py Log: Merged changes from revision 423: Removed separartion of attributes for plain setters from ._create() because .set() does this itself. Minor optimization in kw.items(). Do not pass child-only attributes to the parent. Modified: home/phd/SQLObject/inheritance/sqlobject/main.py =================================================================== --- home/phd/SQLObject/inheritance/sqlobject/main.py 2004-11-30 13:04:38 UTC (rev 423) +++ home/phd/SQLObject/inheritance/sqlobject/main.py 2004-11-30 13:08:08 UTC (rev 424) @@ -887,8 +887,9 @@ is_column = self._SO_plainSetters.has_key f_is_column = lambda item: is_column(item[0]) f_not_column = lambda item: not is_column(item[0]) - extra = dict(filter(f_not_column, kw.items())) - kw = dict(filter(f_is_column, kw.items())) + items = kw.items() + extra = dict(filter(f_not_column, items)) + kw = dict(filter(f_is_column, items)) # _SO_creating is special, see _SO_setValue if self._SO_creating or self._lazyUpdate: @@ -898,7 +899,15 @@ kw[name] = fromPy(value, self._SO_validatorState) setattr(self, instanceName(name), value) for name, value in extra.items(): - setattr(self, name, value) + try: + getattr(self.__class__, name) + except AttributeError: + raise TypeError, "%s.set() got an unexpected keyword argument %s" % (self.__class__.__name__, name) + try: + setattr(self, name, value) + except AttributeError, e: + raise AttributeError, '%s (with attribute %r)' % (e, name) + self._SO_createValues.update(kw) self.dirty = True return @@ -925,7 +934,14 @@ if self._cacheValues: setattr(self, instanceName(name), value) for name, value in extra.items(): - setattr(self, name, value) + try: + getattr(self.__class__, name) + except AttributeError: + raise TypeError, "%s.set() got an unexpected keyword argument %s" % (self.__class__.__name__, name) + try: + setattr(self, name, value) + except AttributeError, e: + raise AttributeError, '%s (with attribute %r)' % (e, name) if toUpdate: args = [(self._SO_columnDict[name].dbName, value) @@ -1002,8 +1018,13 @@ #DSM: If we are the children of an inheritable class, #DSM: we must first create our parent if self._parentClass: - #kw['childName'] = inst._className - self._parent = self._parentClass(kw=kw) + parentClass = self._parentClass + parent_kw = dict( + [(name, value) for (name, value) in kw.items() + if hasattr(parentClass, name) + ] + ) + self._parent = parentClass(kw=parent_kw) self._parent.childName = self._className id = self._parent.id @@ -1033,35 +1054,8 @@ # that keyword: kw[column.name] = default - # We sort out what columns go straight into the database, - # and which ones need setattr() directly here: - forDB = {} - others = {} - for name, value in kw.items(): - if name in self._SO_plainSetters: - forDB[name] = value - #DSM: If this is a call from the child, - #DSM: we must remove the parameter for the database - if fromChild: del kw[name] - elif not fromChild: - #DSM: Only use other items if this isn't a call from the child - others[name] = value + self.set(**kw) - # We take all the straight-to-DB values and use set() to - # set them: - self.set(**forDB) - - # The rest go through setattr(): - for name, value in others.items(): - try: - getattr(self.__class__, name) - except AttributeError: - raise TypeError, "%s() got an unexpected keyword argument %s" % (self.__class__.__name__, name) - try: - setattr(self, name, value) - except AttributeError, e: - raise AttributeError, '%s (with attribute %r)' % (e, name) - # Then we finalize the process: self._SO_finishCreate(id) |
From: <sub...@co...> - 2004-11-30 13:04:40
|
Author: phd Date: 2004-11-30 13:04:38 +0000 (Tue, 30 Nov 2004) New Revision: 423 Modified: trunk/SQLObject/sqlobject/main.py Log: Removed separartion of attributes for plain setters from ._create() because .set() does this itself. Minor optimization in kw.items(). Modified: trunk/SQLObject/sqlobject/main.py =================================================================== --- trunk/SQLObject/sqlobject/main.py 2004-11-30 12:57:41 UTC (rev 422) +++ trunk/SQLObject/sqlobject/main.py 2004-11-30 13:04:38 UTC (rev 423) @@ -762,8 +762,9 @@ is_column = self._SO_plainSetters.has_key f_is_column = lambda item: is_column(item[0]) f_not_column = lambda item: not is_column(item[0]) - extra = dict(filter(f_not_column, kw.items())) - kw = dict(filter(f_is_column, kw.items())) + items = kw.items() + extra = dict(filter(f_not_column, items)) + kw = dict(filter(f_is_column, items)) # _SO_creating is special, see _SO_setValue if self._SO_creating or self._lazyUpdate: @@ -773,7 +774,15 @@ kw[name] = fromPy(value, self._SO_validatorState) setattr(self, instanceName(name), value) for name, value in extra.items(): - setattr(self, name, value) + try: + getattr(self.__class__, name) + except AttributeError: + raise TypeError, "%s.set() got an unexpected keyword argument %s" % (self.__class__.__name__, name) + try: + setattr(self, name, value) + except AttributeError, e: + raise AttributeError, '%s (with attribute %r)' % (e, name) + self._SO_createValues.update(kw) self.dirty = True return @@ -800,7 +809,14 @@ if self._cacheValues: setattr(self, instanceName(name), value) for name, value in extra.items(): - setattr(self, name, value) + try: + getattr(self.__class__, name) + except AttributeError: + raise TypeError, "%s.set() got an unexpected keyword argument %s" % (self.__class__.__name__, name) + try: + setattr(self, name, value) + except AttributeError, e: + raise AttributeError, '%s (with attribute %r)' % (e, name) if toUpdate: args = [(self._SO_columnDict[name].dbName, value) @@ -891,31 +907,8 @@ # that keyword: kw[column.name] = default - # We sort out what columns go straight into the database, - # and which ones need setattr() directly here: - forDB = {} - others = {} - for name, value in kw.items(): - if name in self._SO_plainSetters: - forDB[name] = value - else: - others[name] = value + self.set(**kw) - # We take all the straight-to-DB values and use set() to - # set them: - self.set(**forDB) - - # The rest go through setattr(): - for name, value in others.items(): - try: - getattr(self.__class__, name) - except AttributeError: - raise TypeError, "%s() got an unexpected keyword argument %s" % (self.__class__.__name__, name) - try: - setattr(self, name, value) - except AttributeError, e: - raise AttributeError, '%s (with attribute %r)' % (e, name) - # Then we finalize the process: self._SO_finishCreate(id) |
From: <sub...@co...> - 2004-11-30 12:57:43
|
Author: phd Date: 2004-11-30 12:57:41 +0000 (Tue, 30 Nov 2004) New Revision: 422 Modified: home/phd/SQLObject/inheritance/tests/test_sqlobject.py Log: Merged fix from revision 421: replaced usage() with sys.exit() and an error message. Modified: home/phd/SQLObject/inheritance/tests/test_sqlobject.py =================================================================== --- home/phd/SQLObject/inheritance/tests/test_sqlobject.py 2004-11-30 12:56:55 UTC (rev 421) +++ home/phd/SQLObject/inheritance/tests/test_sqlobject.py 2004-11-30 12:57:41 UTC (rev 422) @@ -1340,7 +1340,7 @@ ["database=", "extra-verbose", "super-verbose", "inserts", "coverage"]) except GetoptError: - usage(1) + sys.exit("Usage: %s [-d|--database all|type] [-v[v]] [--extra-verbose|--super-verbose] [--inserts] [--coverage]" % sys.argv[0]) dbs = [] newArgs = [] |
From: <sub...@co...> - 2004-11-30 12:57:01
|
Author: phd Date: 2004-11-30 12:56:55 +0000 (Tue, 30 Nov 2004) New Revision: 421 Modified: trunk/SQLObject/tests/test_sqlobject.py Log: Fixed a bug: replaced usage() with sys.exit() and an error message. Modified: trunk/SQLObject/tests/test_sqlobject.py =================================================================== --- trunk/SQLObject/tests/test_sqlobject.py 2004-11-29 21:30:03 UTC (rev 420) +++ trunk/SQLObject/tests/test_sqlobject.py 2004-11-30 12:56:55 UTC (rev 421) @@ -1340,7 +1340,7 @@ ["database=", "extra-verbose", "super-verbose", "inserts", "coverage"]) except GetoptError: - usage(1) + sys.exit("Usage: %s [-d|--database all|type] [-v[v]] [--extra-verbose|--super-verbose] [--inserts] [--coverage]" % sys.argv[0]) dbs = [] newArgs = [] |
From: <sub...@co...> - 2004-11-29 21:30:09
|
Author: phd Date: 2004-11-29 21:30:03 +0000 (Mon, 29 Nov 2004) New Revision: 420 Modified: home/phd/SQLObject/inheritance/sqlobject/postgres/pgconnection.py Log: Merged changes 418:419: test the server's version and adapt DROP TABLE (do not use CASCADE in version 7.2 or earlier). Modified: home/phd/SQLObject/inheritance/sqlobject/postgres/pgconnection.py =================================================================== --- home/phd/SQLObject/inheritance/sqlobject/postgres/pgconnection.py 2004-11-29 21:27:38 UTC (rev 419) +++ home/phd/SQLObject/inheritance/sqlobject/postgres/pgconnection.py 2004-11-29 21:30:03 UTC (rev 420) @@ -39,6 +39,9 @@ self.dsn = dsn DBAPI.__init__(self, **kw) + # Server version cache + self._server_version = None # Not yet initialized + def connectionFromURI(cls, uri): user, password, host, path, args = cls._parseURI(uri) path = path.strip('/') @@ -93,6 +96,8 @@ return '%s SERIAL PRIMARY KEY' % soClass._idName def dropTable(self, tableName, cascade=False): + if self.server_version[:3] <= "7.2": + cascade=False self.query("DROP TABLE %s %s" % (tableName, cascade and 'CASCADE' or '')) @@ -198,3 +203,11 @@ else: return col.Col, {} + def server_version(self): + if self._server_version is None: + # The result is something like + # ' PostgreSQL 7.2.1 on i686-pc-linux-gnu, compiled by GCC 2.95.4' + server_version = self.queryOne("SELECT version()")[0] + self._server_version = server_version.split()[1] + return self._server_version + server_version = property(server_version) |
From: <sub...@co...> - 2004-11-29 21:27:40
|
Author: phd Date: 2004-11-29 21:27:38 +0000 (Mon, 29 Nov 2004) New Revision: 419 Modified: trunk/SQLObject/sqlobject/postgres/pgconnection.py Log: Test the server's version and adapt DROP TABLE (do not use CASCADE in version 7.2 or earlier). Modified: trunk/SQLObject/sqlobject/postgres/pgconnection.py =================================================================== --- trunk/SQLObject/sqlobject/postgres/pgconnection.py 2004-11-29 19:19:41 UTC (rev 418) +++ trunk/SQLObject/sqlobject/postgres/pgconnection.py 2004-11-29 21:27:38 UTC (rev 419) @@ -39,6 +39,9 @@ self.dsn = dsn DBAPI.__init__(self, **kw) + # Server version cache + self._server_version = None # Not yet initialized + def connectionFromURI(cls, uri): user, password, host, path, args = cls._parseURI(uri) path = path.strip('/') @@ -93,6 +96,8 @@ return '%s SERIAL PRIMARY KEY' % soClass._idName def dropTable(self, tableName, cascade=False): + if self.server_version[:3] <= "7.2": + cascade=False self.query("DROP TABLE %s %s" % (tableName, cascade and 'CASCADE' or '')) @@ -198,3 +203,11 @@ else: return col.Col, {} + def server_version(self): + if self._server_version is None: + # The result is something like + # ' PostgreSQL 7.2.1 on i686-pc-linux-gnu, compiled by GCC 2.95.4' + server_version = self.queryOne("SELECT version()")[0] + self._server_version = server_version.split()[1] + return self._server_version + server_version = property(server_version) |
From: <sub...@co...> - 2004-11-29 19:19:43
|
Author: phd Date: 2004-11-29 19:19:41 +0000 (Mon, 29 Nov 2004) New Revision: 418 Modified: home/phd/SQLObject/inheritance/sqlobject/main.py home/phd/SQLObject/inheritance/tests/test_sqlobject.py Log: Merged fixes from the trunk. Modified: home/phd/SQLObject/inheritance/sqlobject/main.py =================================================================== --- home/phd/SQLObject/inheritance/sqlobject/main.py 2004-11-29 19:13:59 UTC (rev 417) +++ home/phd/SQLObject/inheritance/sqlobject/main.py 2004-11-29 19:19:41 UTC (rev 418) @@ -895,7 +895,7 @@ for name, value in kw.items(): fromPy = getattr(self, '_SO_fromPython_%s' % name, None) if fromPy: - kw[name] = value = fromPy(value, self._SO_validatorState) + kw[name] = fromPy(value, self._SO_validatorState) setattr(self, instanceName(name), value) for name, value in extra.items(): setattr(self, name, value) Modified: home/phd/SQLObject/inheritance/tests/test_sqlobject.py =================================================================== --- home/phd/SQLObject/inheritance/tests/test_sqlobject.py 2004-11-29 19:13:59 UTC (rev 417) +++ home/phd/SQLObject/inheritance/tests/test_sqlobject.py 2004-11-29 19:19:41 UTC (rev 418) @@ -34,7 +34,7 @@ class Duplicate(SQLObject): pass except ValueError, err: - self.assertEqual(str(err), "class Duplicate is already in the registry") + assert str(err).startswith("class Duplicate is already in the registry") else: self.fail("should have raised an error on duplicate class definition") @@ -1198,14 +1198,9 @@ n += 1 SOIndex1(name=name, number=n) mod = SOIndex1._connection.module - # Firebird doesn't throw an integrity error, unfortunately: - if mod.__name__.endswith('kinterbasdb'): - exc = mod.ProgrammingError - else: - exc = mod.IntegrityError try: SOIndex1(name='blah', number=0) - except exc: + except (mod.ProgrammingError, mod.IntegrityError): # expected pass else: |
From: <sub...@co...> - 2004-11-29 19:14:02
|
Author: phd Date: 2004-11-29 19:13:59 +0000 (Mon, 29 Nov 2004) New Revision: 417 Modified: trunk/SQLObject/sqlobject/main.py Log: dbValue is never used in the snippet. Modified: trunk/SQLObject/sqlobject/main.py =================================================================== --- trunk/SQLObject/sqlobject/main.py 2004-11-29 19:11:36 UTC (rev 416) +++ trunk/SQLObject/sqlobject/main.py 2004-11-29 19:13:59 UTC (rev 417) @@ -770,7 +770,7 @@ for name, value in kw.items(): fromPy = getattr(self, '_SO_fromPython_%s' % name, None) if fromPy: - kw[name] = dbValue = fromPy(value, self._SO_validatorState) + kw[name] = fromPy(value, self._SO_validatorState) setattr(self, instanceName(name), value) for name, value in extra.items(): setattr(self, name, value) |
From: <sub...@co...> - 2004-11-29 19:11:41
|
Author: phd Date: 2004-11-29 19:11:36 +0000 (Mon, 29 Nov 2004) New Revision: 416 Added: trunk/SQLObject/tests/test_sqlobject.py Removed: trunk/SQLObject/tests/test.py Modified: trunk/SQLObject/test Log: Renamed tests/test.py to tests/test_sqlobject.py to allow import from it (and not from python test). Modified: trunk/SQLObject/test =================================================================== --- trunk/SQLObject/test 2004-11-29 19:10:10 UTC (rev 415) +++ trunk/SQLObject/test 2004-11-29 19:11:36 UTC (rev 416) @@ -11,8 +11,8 @@ if [ "$1" = "cover" ] ; then shift sudo python$VERSION setup.py -q install - python$VERSION ./tests/coverage.py -x tests/test.py $* + python$VERSION ./tests/coverage.py -x tests/test_sqlobject.py $* ./tests/coverage.py -a `find tests SQLObject -name '*.py'` else - sudo python$VERSION setup.py -q install && python$VERSION tests/test.py $* + sudo python$VERSION setup.py -q install && python$VERSION tests/test_sqlobject.py $* fi Deleted: trunk/SQLObject/tests/test.py =================================================================== --- trunk/SQLObject/tests/test.py 2004-11-29 19:10:10 UTC (rev 415) +++ trunk/SQLObject/tests/test.py 2004-11-29 19:11:36 UTC (rev 416) @@ -1,1390 +0,0 @@ -""" -Main unit testing for SQLObject. - -Use -vv to see SQL queries, -vvv to also see output from queries, -and together with --inserts to see the SQL from the standard -insert statements (which are often boring). -""" - -from __future__ import generators - -import sys, os -if '--coverage' in sys.argv: - import coverage - print 'Starting coverage' - coverage.erase() - coverage.start() - -from SQLObjectTest import * -from sqlobject import * -from sqlobject.include import validators -from sqlobject import classregistry -from mx import DateTime -global curr_db -curr_db = None -from sqlobject import cache - -class ClassRegistryTest(SQLObjectTest): - def testErrorOnDuplicateClassDefinition(self): - """Raise an error if a class is defined more than once.""" - class Duplicate(SQLObject): - pass - - try: - class Duplicate(SQLObject): - pass - except ValueError, err: - assert str(err).startswith("class Duplicate is already in the registry") - else: - self.fail("should have raised an error on duplicate class definition") - -######################################## -## Basic operation -######################################## - -class TestSO1(SQLObject): - - name = StringCol(length=50, dbName='name_col') - _cacheValues = False - _columns = [ - StringCol('passwd', length=10), - ] - - def _set_passwd(self, passwd): - self._SO_set_passwd(passwd.encode('rot13')) - -class TestCase1(SQLObjectTest): - - classes = [TestSO1] - MyClass = TestSO1 - - info = [('bob', 'god'), ('sally', 'sordid'), - ('dave', 'dremel'), ('fred', 'forgo')] - - def inserts(self): - for name, passwd in self.info: - self.MyClass(name=name, passwd=passwd) - - def testGet(self): - bob = self.MyClass.selectBy(name='bob')[0] - self.assertEqual(bob.name, 'bob') - self.assertEqual(bob.passwd, 'god'.encode('rot13')) - - def testNewline(self): - bob = self.MyClass.selectBy(name='bob')[0] - testString = 'hey\nyou\\can\'t you see me?\t' - bob.name = testString - self.failUnless(bob.name == testString, (bob.name, testString)) - - def testCount(self): - self.assertEqual(self.MyClass.selectBy(name='bob').count(), 1) - self.assertEqual(self.MyClass.select(self.MyClass.q.name == 'bob').count(), 1) - self.assertEqual(self.MyClass.select().count(), len(list(self.MyClass.select()))) - -class TestCaseGetSet(TestCase1): - - def testGet(self): - bob = TestSO1.selectBy(name='bob')[0] - self.assertEqual(bob.name, 'bob') - bob.name = 'joe' - self.assertEqual(bob.name, 'joe') - - -class TestSO2(SQLObject): - name = StringCol(length=50, dbName='name_col') - passwd = StringCol(length=10) - - def _set_passwd(self, passwd): - self._SO_set_passwd(passwd.encode('rot13')) - -class TestCase2(TestCase1): - - classes = [TestSO2] - MyClass = TestSO2 - -class TestSO3(SQLObject): - name = StringCol(length=10, dbName='name_col') - other = ForeignKey('TestSO4', default=None) - other2 = KeyCol(foreignKey='TestSO4', default=None) - -class TestSO4(SQLObject): - me = StringCol(length=10) - -class Student(SQLObject): - is_smart = BoolCol() - -class BoolColTest(SQLObjectTest): - classes = [Student] - - def testBoolCol(self): - student = Student(is_smart=False) - self.assertEqual(student.is_smart, False) - student2 = Student(is_smart='false') - self.assertEqual(student2.is_smart, True) - -class TestCase34(SQLObjectTest): - - classes = [TestSO4, TestSO3] - - def testForeignKey(self): - tc3 = TestSO3(name='a') - self.assertEqual(tc3.other, None) - self.assertEqual(tc3.other2, None) - self.assertEqual(tc3.otherID, None) - self.assertEqual(tc3.other2ID, None) - tc4a = TestSO4(me='1') - tc3.other = tc4a - self.assertEqual(tc3.other, tc4a) - self.assertEqual(tc3.otherID, tc4a.id) - tc4b = TestSO4(me='2') - tc3.other = tc4b.id - self.assertEqual(tc3.other, tc4b) - self.assertEqual(tc3.otherID, tc4b.id) - tc4c = TestSO4(me='3') - tc3.other2 = tc4c - self.assertEqual(tc3.other2, tc4c) - self.assertEqual(tc3.other2ID, tc4c.id) - tc4d = TestSO4(me='4') - tc3.other2 = tc4d.id - self.assertEqual(tc3.other2, tc4d) - self.assertEqual(tc3.other2ID, tc4d.id) - tcc = TestSO3(name='b', other=tc4a) - self.assertEqual(tcc.other, tc4a) - tcc2 = TestSO3(name='c', other=tc4a.id) - self.assertEqual(tcc2.other, tc4a) - -class TestSO5(SQLObject): - name = StringCol(length=10, dbName='name_col') - other = ForeignKey('TestSO6', default=None, cascade=True) - another = ForeignKey('TestSO7', default=None, cascade=True) - -class TestSO6(SQLObject): - name = StringCol(length=10, dbName='name_col') - other = ForeignKey('TestSO7', default=None, cascade=True) - -class TestSO7(SQLObject): - name = StringCol(length=10, dbName='name_col') - -class TestCase567(SQLObjectTest): - - classes = [TestSO7, TestSO6, TestSO5] - - def testForeignKeyDestroySelfCascade(self): - tc5 = TestSO5(name='a') - tc6a = TestSO6(name='1') - tc5.other = tc6a - tc7a = TestSO7(name='2') - tc6a.other = tc7a - tc5.another = tc7a - self.assertEqual(tc5.other, tc6a) - self.assertEqual(tc5.otherID, tc6a.id) - self.assertEqual(tc6a.other, tc7a) - self.assertEqual(tc6a.otherID, tc7a.id) - self.assertEqual(tc5.other.other, tc7a) - self.assertEqual(tc5.other.otherID, tc7a.id) - self.assertEqual(tc5.another, tc7a) - self.assertEqual(tc5.anotherID, tc7a.id) - self.assertEqual(tc5.other.other, tc5.another) - self.assertEqual(TestSO5.select().count(), 1) - self.assertEqual(TestSO6.select().count(), 1) - self.assertEqual(TestSO7.select().count(), 1) - tc6b = TestSO6(name='3') - tc6c = TestSO6(name='4') - tc7b = TestSO7(name='5') - tc6b.other = tc7b - tc6c.other = tc7b - self.assertEqual(TestSO5.select().count(), 1) - self.assertEqual(TestSO6.select().count(), 3) - self.assertEqual(TestSO7.select().count(), 2) - tc6b.destroySelf() - self.assertEqual(TestSO5.select().count(), 1) - self.assertEqual(TestSO6.select().count(), 2) - self.assertEqual(TestSO7.select().count(), 2) - tc7b.destroySelf() - self.assertEqual(TestSO5.select().count(), 1) - self.assertEqual(TestSO6.select().count(), 1) - self.assertEqual(TestSO7.select().count(), 1) - tc7a.destroySelf() - self.assertEqual(TestSO5.select().count(), 0) - self.assertEqual(TestSO6.select().count(), 0) - self.assertEqual(TestSO7.select().count(), 0) - - def testForeignKeyDropTableCascade(self): - if curr_db == 'sybase': - # XXX This test doesn't pass with sybase. - return - tc5a = TestSO5(name='a') - tc6a = TestSO6(name='1') - tc5a.other = tc6a - tc7a = TestSO7(name='2') - tc6a.other = tc7a - tc5a.another = tc7a - tc5b = TestSO5(name='b') - tc5c = TestSO5(name='c') - tc6b = TestSO6(name='3') - tc5c.other = tc6b - self.assertEqual(TestSO5.select().count(), 3) - self.assertEqual(TestSO6.select().count(), 2) - self.assertEqual(TestSO7.select().count(), 1) - TestSO7.dropTable(cascade=True) - self.assertEqual(TestSO5.select().count(), 3) - self.assertEqual(TestSO6.select().count(), 2) - tc6a.destroySelf() - self.assertEqual(TestSO5.select().count(), 2) - self.assertEqual(TestSO6.select().count(), 1) - tc6b.destroySelf() - self.assertEqual(TestSO5.select().count(), 1) - self.assertEqual(TestSO6.select().count(), 0) - self.assertEqual(iter(TestSO5.select()).next(), tc5b) - tc6c = TestSO6(name='3') - tc5b.other = tc6c - self.assertEqual(TestSO5.select().count(), 1) - self.assertEqual(TestSO6.select().count(), 1) - tc6c.destroySelf() - self.assertEqual(TestSO5.select().count(), 0) - self.assertEqual(TestSO6.select().count(), 0) - -class TestSO8(SQLObject): - name = StringCol(length=10, dbName='name_col') - other = ForeignKey('TestSO9', default=None, cascade=False) - -class TestSO9(SQLObject): - name = StringCol(length=10, dbName='name_col') - -class TestCase89(SQLObjectTest): - - classes = [TestSO9, TestSO8] - - def testForeignKeyDestroySelfRestrict(self): - tc8a = TestSO8(name='a') - tc9a = TestSO9(name='1') - tc8a.other = tc9a - tc8b = TestSO8(name='b') - tc9b = TestSO9(name='2') - self.assertEqual(tc8a.other, tc9a) - self.assertEqual(tc8a.otherID, tc9a.id) - self.assertEqual(TestSO8.select().count(), 2) - self.assertEqual(TestSO9.select().count(), 2) - self.assertRaises(Exception, tc9a.destroySelf) - tc9b.destroySelf() - self.assertEqual(TestSO8.select().count(), 2) - self.assertEqual(TestSO9.select().count(), 1) - tc8a.destroySelf() - tc8b.destroySelf() - tc9a.destroySelf() - self.assertEqual(TestSO8.select().count(), 0) - self.assertEqual(TestSO9.select().count(), 0) - -######################################## -## Fancy sort -######################################## - -class Names(SQLObject): - - _table = 'names_table' - - firstName = StringCol(length=30) - lastName = StringCol(length=30) - - _defaultOrder = ['lastName', 'firstName'] - -class NamesTest(SQLObjectTest): - - classes = [Names] - - def inserts(self): - for firstName, lastName in [('aj', 'baker'), ('joe', 'robbins'), - ('tim', 'jackson'), ('joe', 'baker'), - ('zoe', 'robbins')]: - Names(firstName=firstName, lastName=lastName) - - def testDefaultOrder(self): - self.assertEqual([(n.firstName, n.lastName) for n in Names.select()], - [('aj', 'baker'), ('joe', 'baker'), - ('tim', 'jackson'), ('joe', 'robbins'), - ('zoe', 'robbins')]) - - def testOtherOrder(self): - self.assertEqual([(n.firstName, n.lastName) for n in Names.select().orderBy(['firstName', 'lastName'])], - [('aj', 'baker'), ('joe', 'baker'), - ('joe', 'robbins'), ('tim', 'jackson'), - ('zoe', 'robbins')]) - - def testUntranslatedColumnOrder(self): - self.assertEqual([(n.firstName, n.lastName) for n in Names.select().orderBy(['first_name', 'last_name'])], - [('aj', 'baker'), ('joe', 'baker'), - ('joe', 'robbins'), ('tim', 'jackson'), - ('zoe', 'robbins')]) - - def testSingleUntranslatedColumnOrder(self): - self.assertEqual([n.firstName for n in - Names.select().orderBy('firstName')], - ['aj', 'joe', 'joe', 'tim', 'zoe']) - self.assertEqual([n.firstName for n in - Names.select().orderBy('first_name')], - ['aj', 'joe', 'joe', 'tim', 'zoe']) - self.assertEqual([n.firstName for n in - Names.select().orderBy('-firstName')], - ['zoe', 'tim', 'joe', 'joe', 'aj']) - self.assertEqual([n.firstName for n in - Names.select().orderBy('-first_name')], - ['zoe', 'tim', 'joe', 'joe', 'aj']) - self.assertEqual([n.firstName for n in - Names.select().orderBy(Names.q.firstName)], - ['aj', 'joe', 'joe', 'tim', 'zoe']) - -######################################## -## Select results -######################################## - -class IterTest(SQLObject): - name = StringCol(dbName='name_col') - -class IterationTestCase(SQLObjectTest): - '''Test basic iteration techniques''' - - classes = [IterTest] - - names = ('a', 'b', 'c') - - def inserts(self): - for name in self.names: - IterTest(name=name) - - def test_00_normal(self): - count = 0 - for test in IterTest.select(): - count += 1 - self.failIf(count != len(self.names)) - - def test_01_turn_to_list(self): - count = 0 - for test in list(IterTest.select()): - count += 1 - self.failIf(count != len(self.names)) - - def test_02_generator(self): - def enumerate(iterable): - i = 0 - for obj in iterable: - yield i, obj - i += 1 - all = IterTest.select() - count = 0 - for i, test in enumerate(all): - count += 1 - self.failIf(count != len(self.names)) - - def test_03_ranged_indexed(self): - all = IterTest.select() - count = 0 - for i in range(all.count()): - test = all[i] - count += 1 - self.failIf(count != len(self.names)) - - def test_04_indexed_ended_by_exception(self): - all = IterTest.select() - count = 0 - try: - while 1: - test = all[count] - count = count+1 - # Stop the test if it's gone on too long - if count > len(self.names): - break - except IndexError: - pass - self.assertEqual(count, len(self.names)) - - -######################################## -## Delete during select -######################################## - - -class DeleteSelectTest(TestCase1): - - def testGet(self): - return - - def testSelect(self): - for obj in TestSO1.select('all'): - obj.destroySelf() - self.assertEqual(list(TestSO1.select('all')), []) - -######################################## -## Delete without caching -######################################## - -class NoCache(SQLObject): - name = StringCol() - -class TestNoCache(SQLObjectTest): - - classes=[NoCache] - - def setUp(self): - SQLObjectTest.setUp(self) - NoCache._connection.cache = cache.CacheSet(cache=False) - - def tearDown(self): - NoCache._connection.cache = cache.CacheSet(cache=True) - SQLObjectTest.tearDown(self) - - def testDestroySelf(self): - value = NoCache(name='test') - value.destroySelf() - -######################################## -## Transaction test -######################################## - -class TestSOTrans(SQLObject): - #_cacheValues = False - name = StringCol(length=10, alternateID=True, dbName='name_col') - _defaultOrderBy = 'name' - -class TransactionTest(SQLObjectTest): - - classes = [TestSOTrans] - - def inserts(self): - TestSOTrans(name='bob') - TestSOTrans(name='tim') - - def testTransaction(self): - if not self.supportTransactions: return - trans = TestSOTrans._connection.transaction() - try: - TestSOTrans._connection.autoCommit = 'exception' - TestSOTrans(name='joe', connection=trans) - trans.rollback() - trans.begin() - self.assertEqual([n.name for n in TestSOTrans.select(connection=trans)], - ['bob', 'tim']) - b = TestSOTrans.byName('bob', connection=trans) - b.name = 'robert' - trans.commit() - self.assertEqual(b.name, 'robert') - b.name = 'bob' - trans.rollback() - trans.begin() - self.assertEqual(b.name, 'robert') - finally: - TestSOTrans._connection.autoCommit = True - - -######################################## -## Enum test -######################################## - -class Enum1(SQLObject): - - _columns = [ - EnumCol('l', enumValues=['a', 'bcd', 'e']), - ] - -class TestEnum1(SQLObjectTest): - - classes = [Enum1] - - def inserts(self): - for l in ['a', 'bcd', 'a', 'e']: - Enum1(l=l) - - def testBad(self): - if self.supportRestrictedEnum: - try: - v = Enum1(l='b') - except Exception, e: - pass - else: - print v - assert 0, "This should cause an error" - - -######################################## -## Slicing tests -######################################## - -class Counter(SQLObject): - - _columns = [ - IntCol('number', notNull=True), - ] - -class SliceTest(SQLObjectTest): - - classes = [Counter] - - def inserts(self): - for i in range(100): - Counter(number=i) - - def counterEqual(self, counters, value): - self.assertEquals([c.number for c in counters], value) - - def test1(self): - self.counterEqual(Counter.select('all', orderBy='number'), range(100)) - - def test2(self): - self.counterEqual(Counter.select('all', orderBy='number')[10:20], - range(10, 20)) - - def test3(self): - self.counterEqual(Counter.select('all', orderBy='number')[20:30][:5], - range(20, 25)) - - def test4(self): - self.counterEqual(Counter.select('all', orderBy='number')[:-10], - range(0, 90)) - - def test5(self): - self.counterEqual(Counter.select('all', orderBy='number', reversed=True), range(99, -1, -1)) - - def test6(self): - self.counterEqual(Counter.select('all', orderBy='-number'), range(99, -1, -1)) - - -######################################## -## Select tests -######################################## - -class Counter2(SQLObject): - - _columns = [ - IntCol('n1', notNull=True), - IntCol('n2', notNull=True), - ] - -class SelectTest(SQLObjectTest): - - classes = [Counter2] - - def inserts(self): - for i in range(10): - for j in range(10): - Counter2(n1=i, n2=j) - - def counterEqual(self, counters, value): - self.assertEquals([(c.n1, c.n2) for c in counters], value) - - def accumulateEqual(self, func, counters, value): - self.assertEqual(func([ c.n1 for c in counters]), value) - - def test1(self): - self.accumulateEqual(sum,Counter2.select(orderBy='n1'), - sum(range(10)) * 10) - - def test2(self): - self.accumulateEqual(len,Counter2.select('all'), 100) - - -######################################## -## Dynamic column tests -######################################## - -class Person(SQLObject): - - _columns = [StringCol('name', length=100, dbName='name_col')] - _defaultOrder = 'name' - -class Phone(SQLObject): - - _columns = [StringCol('phone', length=12)] - _defaultOrder = 'phone' - -class PeopleTest(SQLObjectTest): - - classes = [Person, Phone] - - def inserts(self): - for n in ['jane', 'tim', 'bob', 'jake']: - Person(name=n) - for p in ['555-555-5555', '555-394-2930', - '444-382-4854']: - Phone(phone=p) - - def testDefaultOrder(self): - self.assertEqual(list(Person.select('all')), - list(Person.select('all', orderBy=Person._defaultOrder))) - - def testDynamicColumn(self): - if not self.supportDynamic: - return - nickname = StringCol('nickname', length=10) - Person.addColumn(nickname, changeSchema=True) - n = Person(name='robert', nickname='bob') - self.assertEqual([p.name for p in Person.select('all')], - ['bob', 'jake', 'jane', 'robert', 'tim']) - Person.delColumn(nickname, changeSchema=True) - - def testDynamicJoin(self): - if not self.supportDynamic: - return - col = KeyCol('personID', foreignKey='Person') - Phone.addColumn(col, changeSchema=True) - join = MultipleJoin('Phone') - Person.addJoin(join) - for phone in Phone.select('all'): - if phone.phone.startswith('555'): - phone.person = Person.selectBy(name='tim')[0] - 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) - -######################################## -## Auto class generation -######################################## - -class AutoTest(SQLObjectTest): - - mysqlCreate = """ - CREATE TABLE IF NOT EXISTS auto_test ( - auto_id INT AUTO_INCREMENT PRIMARY KEY, - first_name VARCHAR(100), - last_name VARCHAR(200) NOT NULL, - age INT DEFAULT NULL, - created DATETIME NOT NULL, - happy char(1) DEFAULT 'Y' NOT NULL, - wannahavefun TINYINT DEFAULT 0 NOT NULL - ) - """ - - postgresCreate = """ - CREATE TABLE auto_test ( - auto_id SERIAL PRIMARY KEY, - first_name VARCHAR(100), - last_name VARCHAR(200) NOT NULL, - age INT DEFAULT 0, - created VARCHAR(40) NOT NULL, - happy char(1) DEFAULT 'Y' NOT NULL, - wannahavefun BOOL DEFAULT FALSE NOT NULL - ) - """ - - sybaseCreate = """ - CREATE TABLE auto_test ( - auto_id integer, - first_name VARCHAR(100), - last_name VARCHAR(200) NOT NULL, - age INT DEFAULT 0, - created VARCHAR(40) NOT NULL, - happy char(1) DEFAULT 'Y' NOT NULL - ) - """ - - mysqlDrop = """ - DROP TABLE IF EXISTS auto_test - """ - - postgresDrop = """ - DROP TABLE auto_test - """ - - sybaseDrop = """ - DROP TABLE auto_test - """ - - _table = 'auto_test' - - def testClassCreate(self): - if not self.supportAuto: - return - class AutoTest(SQLObject): - _fromDatabase = True - _idName = 'auto_id' - _connection = connection() - john = AutoTest(firstName='john', - lastName='doe', - age=10, - created=DateTime.now(), - wannahavefun=False) - jane = AutoTest(firstName='jane', - lastName='doe', - happy='N', - created=DateTime.now(), - wannahavefun=True) - self.failIf(john.wannahavefun) - self.failUnless(jane.wannahavefun) - del classregistry.registry(AutoTest._registry).classes['AutoTest'] - -######################################## -## Joins -######################################## - -class PersonJoiner(SQLObject): - - _columns = [StringCol('name', length=40, alternateID=True)] - _joins = [RelatedJoin('AddressJoiner')] - -class AddressJoiner(SQLObject): - - _columns = [StringCol('zip', length=5, alternateID=True)] - _joins = [RelatedJoin('PersonJoiner')] - -class ImplicitJoiningSO(SQLObject): - foo = RelatedJoin('Bar') - -class ExplicitJoiningSO(SQLObject): - _joins = [MultipleJoin('Bar', joinMethodName='foo')] - -class JoinTest(SQLObjectTest): - - classes = [PersonJoiner, AddressJoiner] - - def inserts(self): - for n in ['bob', 'tim', 'jane', 'joe', 'fred', 'barb']: - PersonJoiner(name=n) - for z in ['11111', '22222', '33333', '44444']: - AddressJoiner(zip=z) - - def testJoin(self): - b = PersonJoiner.byName('bob') - self.assertEqual(b.addressJoiners, []) - z = AddressJoiner.byZip('11111') - b.addAddressJoiner(z) - self.assertZipsEqual(b.addressJoiners, ['11111']) - self.assertNamesEqual(z.personJoiners, ['bob']) - z2 = AddressJoiner.byZip('22222') - b.addAddressJoiner(z2) - self.assertZipsEqual(b.addressJoiners, ['11111', '22222']) - self.assertNamesEqual(z2.personJoiners, ['bob']) - b.removeAddressJoiner(z) - self.assertZipsEqual(b.addressJoiners, ['22222']) - self.assertNamesEqual(z.personJoiners, []) - - def assertZipsEqual(self, zips, dest): - self.assertEqual([a.zip for a in zips], dest) - - def assertNamesEqual(self, people, dest): - self.assertEqual([p.name for p in people], dest) - - def testJoinAttributeWithUnderscores(self): - # Make sure that the implicit setting of joinMethodName works - self.failUnless(hasattr(ImplicitJoiningSO, 'foo')) - self.failIf(hasattr(ImplicitJoiningSO, 'bars')) - - # And make sure explicit setting also works - self.failUnless(hasattr(ExplicitJoiningSO, 'foo')) - self.failIf(hasattr(ExplicitJoiningSO, 'bars')) - -class PersonJoiner2(SQLObject): - - _columns = [StringCol('name', length=40, alternateID=True)] - _joins = [MultipleJoin('AddressJoiner2')] - -class AddressJoiner2(SQLObject): - - _columns = [StringCol('zip', length=5), - StringCol('plus4', length=4, default=None), - ForeignKey('PersonJoiner2')] - _defaultOrder = ['-zip', 'plus4'] - -class JoinTest2(SQLObjectTest): - - classes = [PersonJoiner2, AddressJoiner2] - - def inserts(self): - p1 = PersonJoiner2(name='bob') - p2 = PersonJoiner2(name='sally') - for z in ['11111', '22222', '33333']: - a = AddressJoiner2(zip=z, personJoiner2=p1) - #p1.addAddressJoiner2(a) - AddressJoiner2(zip='00000', personJoiner2=p2) - - def test(self): - bob = PersonJoiner2.byName('bob') - sally = PersonJoiner2.byName('sally') - self.assertEqual(len(bob.addressJoiner2s), 3) - self.assertEqual(len(sally.addressJoiner2s), 1) - bob.addressJoiner2s[0].destroySelf() - self.assertEqual(len(bob.addressJoiner2s), 2) - z = bob.addressJoiner2s[0] - z.zip = 'xxxxx' - id = z.id - del z - z = AddressJoiner2.get(id) - self.assertEqual(z.zip, 'xxxxx') - - def testDefaultOrder(self): - p1 = PersonJoiner2.byName('bob') - self.assertEqual([i.zip for i in p1.addressJoiner2s], - ['33333', '22222', '11111']) - - -######################################## -## Inheritance -######################################## - -class Super(SQLObject): - - _columns = [StringCol('name', length=10)] - -class Sub(Super): - - _columns = Super._columns + [StringCol('name2', length=10)] - -class InheritanceTest(SQLObjectTest): - - classes = [Super, Sub] - - def testSuper(self): - s1 = Super(name='one') - s2 = Super(name='two') - s3 = Super.get(s1.id) - self.assertEqual(s1, s3) - - def testSub(self): - s1 = Sub(name='one', name2='1') - s2 = Sub(name='two', name2='2') - s3 = Sub.get(s1.id) - self.assertEqual(s1, s3) - - -######################################## -## Expiring, syncing -######################################## - -class SyncTest(SQLObject): - name = StringCol(length=50, alternateID=True, dbName='name_col') - -class ExpireTest(SQLObjectTest): - - classes = [SyncTest] - - def inserts(self): - SyncTest(name='bob') - SyncTest(name='tim') - - def testExpire(self): - conn = SyncTest._connection - b = SyncTest.byName('bob') - conn.query("UPDATE sync_test SET name_col = 'robert' WHERE id = %i" - % b.id) - self.assertEqual(b.name, 'bob') - b.expire() - self.assertEqual(b.name, 'robert') - conn.query("UPDATE sync_test SET name_col = 'bobby' WHERE id = %i" - % b.id) - b.sync() - self.assertEqual(b.name, 'bobby') - -######################################## -## Validation/conversion -######################################## - -class SOValidation(SQLObject): - - name = StringCol(validator=validators.PlainText(), default='x', dbName='name_col') - name2 = StringCol(validator=validators.ConfirmType(str), default='y') - name3 = IntCol(validator=validators.Wrapper(fromPython=int), default=100) - -class ValidationTest(SQLObjectTest): - - classes = [SOValidation] - - def testValidate(self): - t = SOValidation(name='hey') - self.assertRaises(validators.InvalidField, setattr, t, - 'name', '!!!') - t.name = 'you' - - def testConfirmType(self): - t = SOValidation(name2='hey') - self.assertRaises(validators.InvalidField, setattr, t, - 'name2', 1) - t.name2 = 'you' - - def testWrapType(self): - t = SOValidation(name3=1) - self.assertRaises(validators.InvalidField, setattr, t, - 'name3', 'x') - t.name3 = 1L - self.assertEqual(t.name3, 1) - t.name3 = '1' - self.assertEqual(t.name3, 1) - t.name3 = 0 - self.assertEqual(t.name3, 0) - - -######################################## -## String ID test -######################################## - -class SOStringID(SQLObject): - - _table = 'so_string_id' - _idType = str - val = StringCol(alternateID=True) - - mysqlCreate = """ - CREATE TABLE IF NOT EXISTS so_string_id ( - id VARCHAR(50) PRIMARY KEY, - val TEXT - ) - """ - - postgresCreate = """ - CREATE TABLE so_string_id ( - id VARCHAR(50) PRIMARY KEY, - val TEXT - ) - """ - - sybaseCreate = """ - CREATE TABLE so_string_id ( - id VARCHAR(50) UNIQUE, - val VARCHAR(50) NULL - ) - """ - - firebirdCreate = """ - CREATE TABLE so_string_id ( - id VARCHAR(50) NOT NULL PRIMARY KEY, - val BLOB SUB_TYPE TEXT - ) - """ - - sqliteCreate = postgresCreate - - mysqlDrop = """ - DROP TABLE IF EXISTS so_string_id - """ - - postgresDrop = """ - DROP TABLE so_string_id - """ - - sqliteDrop = postgresDrop - firebirdDrop = postgresDrop - -class StringIDTest(SQLObjectTest): - - classes = [SOStringID] - - def testStringID(self): - t = SOStringID(id='hey', val='whatever') - t2 = SOStringID.byVal('whatever') - self.assertEqual(t, t2) - t3 = SOStringID(id='you', val='nowhere') - t4 = SOStringID.get('you') - self.assertEqual(t3, t4) - - - -class AnotherStyle(MixedCaseUnderscoreStyle): - def pythonAttrToDBColumn(self, attr): - if attr.lower().endswith('id'): - return 'id'+MixedCaseUnderscoreStyle.pythonAttrToDBColumn(self, attr[:-2]) - else: - return MixedCaseUnderscoreStyle.pythonAttrToDBColumn(self, attr) - -class SOStyleTest1(SQLObject): - a = StringCol() - st2 = ForeignKey('SOStyleTest2') - _style = AnotherStyle() - -class SOStyleTest2(SQLObject): - b = StringCol() - _style = AnotherStyle() - -class StyleTest(SQLObjectTest): - - classes = [SOStyleTest2, SOStyleTest1] - - - def test(self): - st1 = SOStyleTest1(a='something', st2=None) - st2 = SOStyleTest2(b='whatever') - st1.st2 = st2 - self.assertEqual(st1._SO_columnDict['st2ID'].dbName, 'idst2') - self.assertEqual(st1.st2, st2) - -######################################## -## Lazy updates -######################################## - -class Lazy(SQLObject): - - _lazyUpdate = True - name = StringCol() - other = StringCol(default='nothing') - third = StringCol(default='third') - -class LazyTest(SQLObjectTest): - - classes = [Lazy] - - def setUp(self): - # All this stuff is so that we can track when the connection - # does an actual update; we put in a new _SO_update method - # that calls the original and sets an instance variable that - # we can later check. - SQLObjectTest.setUp(self) - self.conn = Lazy._connection - self.conn.didUpdate = False - self._oldUpdate = self.conn._SO_update - newUpdate = lambda so, values, s=self, c=self.conn, o=self._oldUpdate: self._alternateUpdate(so, values, c, o) - self.conn._SO_update = newUpdate - - def tearDown(self): - self.conn._SO_update = self._oldUpdate - del self._oldUpdate - - def _alternateUpdate(self, so, values, conn, oldUpdate): - conn.didUpdate = True - return oldUpdate(so, values) - - def test(self): - assert not self.conn.didUpdate - obj = Lazy(name='tim') - # We just did an insert, but not an update: - assert not self.conn.didUpdate - obj.set(name='joe') - assert obj.dirty - self.assertEqual(obj.name, 'joe') - assert not self.conn.didUpdate - obj.syncUpdate() - self.assertEqual(obj.name, 'joe') - assert self.conn.didUpdate - assert not obj.dirty - self.assertEqual(obj.name, 'joe') - self.conn.didUpdate = False - - obj = Lazy(name='frank') - obj.name = 'joe' - assert not self.conn.didUpdate - assert obj.dirty - self.assertEqual(obj.name, 'joe') - obj.name = 'joe2' - assert not self.conn.didUpdate - assert obj.dirty - self.assertEqual(obj.name, 'joe2') - obj.syncUpdate() - self.assertEqual(obj.name, 'joe2') - assert not obj.dirty - assert self.conn.didUpdate - self.conn.didUpdate = False - - obj = Lazy(name='loaded') - assert not obj.dirty - assert not self.conn.didUpdate - self.assertEqual(obj.name, 'loaded') - obj.name = 'unloaded' - assert obj.dirty - self.assertEqual(obj.name, 'unloaded') - assert not self.conn.didUpdate - obj.sync() - assert not obj.dirty - self.assertEqual(obj.name, 'unloaded') - assert self.conn.didUpdate - self.conn.didUpdate = False - obj.name = 'whatever' - assert obj.dirty - self.assertEqual(obj.name, 'whatever') - assert not self.conn.didUpdate - obj._SO_loadValue('name') - assert obj.dirty - self.assertEqual(obj.name, 'whatever') - assert not self.conn.didUpdate - obj._SO_loadValue('other') - self.assertEqual(obj.name, 'whatever') - assert not self.conn.didUpdate - obj.syncUpdate() - assert self.conn.didUpdate - self.conn.didUpdate = False - - # Now, check that get() doesn't screw - # cached objects' validator state. - obj_id = obj.id - old_state = obj._SO_validatorState - obj = Lazy.get(obj_id) - assert not obj.dirty - assert not self.conn.didUpdate - assert obj._SO_validatorState is old_state - self.assertEqual(obj.name, 'whatever') - obj.name = 'unloaded' - self.assertEqual(obj.name, 'unloaded') - assert obj.dirty - assert not self.conn.didUpdate - # Fetch the object again with get() and - # make sure dirty is still set, as the - # object should come from the cache. - obj = Lazy.get(obj_id) - assert obj.dirty - assert not self.conn.didUpdate - self.assertEqual(obj.name, 'unloaded') - obj.syncUpdate() - assert self.conn.didUpdate - assert not obj.dirty - self.conn.didUpdate = False - - # Then clear the cache, and try a get() - # again, to make sure stuf like _SO_createdValues - # is properly initialized. - self.conn.cache.clear() - obj = Lazy.get(obj_id) - assert not obj.dirty - assert not self.conn.didUpdate - self.assertEqual(obj.name, 'unloaded') - obj.name = 'spongebob' - self.assertEqual(obj.name, 'spongebob') - assert obj.dirty - assert not self.conn.didUpdate - obj.syncUpdate() - assert self.conn.didUpdate - assert not obj.dirty - self.conn.didUpdate = False - - obj = Lazy(name='last') - assert not obj.dirty - obj.syncUpdate() - assert not self.conn.didUpdate - assert not obj.dirty - # Check that setting multiple values - # actually works. This was broken - # and just worked because we were testing - # only one value at a time, so 'name' - # had the right value after the for loop *wink* - # Also, check that passing a name that is not - # a valid column doesn't break, but instead - # just does a plain setattr. - obj.set(name='first', other='who', - third='yes', driver='james') - self.assertEqual(obj.name, 'first') - self.assertEqual(obj.other, 'who') - self.assertEqual(obj.third, 'yes') - self.assertEqual(obj.driver, 'james') - assert obj.dirty - assert not self.conn.didUpdate - obj.syncUpdate() - assert self.conn.didUpdate - assert not obj.dirty - - -######################################## -## Indexes -######################################## - -class SOIndex1(SQLObject): - name = StringCol(length=100) - number = IntCol() - - nameIndex = DatabaseIndex('name', unique=True) - nameIndex2 = DatabaseIndex(name, number) - nameIndex3 = DatabaseIndex({'column': name, - 'length': 3}) - -class SOIndex2(SQLObject): - - name = StringCol() - - nameIndex = DatabaseIndex({'expression': 'lower(name)'}) - -class IndexTest1(SQLObjectTest): - - classes = [SOIndex1] - - def test(self): - n = 0 - for name in 'blah blech boring yep yort snort'.split(): - n += 1 - SOIndex1(name=name, number=n) - mod = SOIndex1._connection.module - try: - SOIndex1(name='blah', number=0) - except (mod.ProgrammingError, mod.IntegrityError): - # expected - pass - else: - assert 0, "Exception expected." - -class IndexTest2(SQLObjectTest): - - classes = [SOIndex2] - - requireSupport = ('supportExpressionIndex',) - - def test(self): - # Not much to test, just want to make sure the table works - # properly - if not self.hasSupport(): - return - SOIndex2(name='') - - -######################################## -## Distinct -######################################## - -class Distinct1(SQLObject): - n = IntCol() - -class Distinct2(SQLObject): - other = ForeignKey('Distinct1') - -class DistinctTest(SQLObjectTest): - - classes = [Distinct1, Distinct2] - - def inserts(self): - obs = [Distinct1(n=i) for i in range(3)] - Distinct2(other=obs[0]) - Distinct2(other=obs[0]) - Distinct2(other=obs[1]) - - def count(self, select): - result = {} - for ob in select: - result[int(ob.n)] = result.get(int(ob.n), 0)+1 - return result - - def testDistinct(self): - query = (Distinct2.q.otherID==Distinct1.q.id) - sel = Distinct1.select(query) - self.assertEqual(self.count(sel), - {0: 2, 1: 1}) - sel = Distinct1.select(query, distinct=True) - self.assertEqual(self.count(sel), - {0: 1, 1:1}) - -######################################## -## Run from command-line: -######################################## - -def coverModules(): - sys.stdout.write('Writing coverage...') - sys.stdout.flush() - here = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - from SQLObject import DBConnection as tmp - there = os.path.dirname(os.path.abspath(tmp.__file__)) - for name, mod in sys.modules.items(): - if not mod: - continue - try: - modFile = os.path.abspath(mod.__file__) - except AttributeError: - # Probably a C extension - continue - if modFile.startswith(here) or modFile.startswith(there): - writeCoverage(mod, there, os.path.join(here, 'SQLObject')) - coverage.erase() - sys.stdout.write('done.\n') - - -def writeCoverage(module, oldBase, newBase): - filename, numbers, unexecuted, s = coverage.analysis(module) - coverFilename = filename + ',cover' - if coverFilename.startswith(oldBase): - coverFilename = newBase + coverFilename[len(oldBase):] - fout = open(coverFilename, 'w') - fin = open(filename) - i = 1 - lines = 0 - good = 0 - while 1: - line = fin.readline() - if not line: break - assert line[-1] == '\n' - fout.write(line[:-1]) - unused = i in unexecuted - interesting = interestingLine(line, unused) - if interesting: - if unused: - fout.write(' '*(72-len(line))) - fout.write('#@@@@') - lastUnused = True - else: - lastUnused = False - good += 1 - lines += 1 - fout.write('\n') - i += 1 - fout.write('\n# Coverage:\n') - fout.write('# %i/%i, %i%%' % ( - good, lines, lines and int(good*100/lines))) - fout.close() - fin.close() - -def interestingLine(line, unused): - line = line.strip() - if not line: - return False - if line.startswith('#'): - return False - if line in ('"""', '"""'): - return False - if line.startswith('global '): - return False - if line.startswith('def ') and not unused: - # If a def *isn't* executed, that's interesting - return False - if line.startswith('class ') and not unused: - return False - return True - - -def main(): - import unittest - from getopt import getopt, GetoptError - - try: - options, arguments = getopt(sys.argv[1:], "d:v", - ["database=", "extra-verbose", "super-verbose", - "inserts", "coverage"]) - except GetoptError: - usage(1) - - dbs = [] - newArgs = [] - doCoverage = False - verbose = 0 - - for option, value in options: - if option in ('-d', '--database'): - dbs.append(value) - elif option == '--inserts': - SQLObjectTest.debugInserts = True - elif option == '--coverage': - # Handled earlier, so we get better coverage - doCoverage = True - elif option == '--extra-verbose': - verbose = 1 - elif option == '--super-verbose': - verbose = 2 - elif option == '-v': - verbose += 1 - - if verbose >= 1: - SQLObjectTest.debugSQL = True - if verbose >= 2: - SQLObjectTest.debugOutput = True - newArgs.append('-vv') - newArgs.extend(arguments) - - sys.argv = [sys.argv[0]] + newArgs - if not dbs: - dbs = ['mysql'] - if dbs == ['all']: - dbs = supportedDatabases() - for db in dbs: - print 'Testing %s' % db - curr_db = db - setDatabaseType(db) - try: - unittest.main() - except SystemExit: - pass - if doCoverage: - coverage.stop() - coverModules() - -if __name__ == '__main__': - main() Copied: trunk/SQLObject/tests/test_sqlobject.py (from rev 415, trunk/SQLObject/tests/test.py) |
From: <sub...@co...> - 2004-11-29 19:10:14
|
Author: phd Date: 2004-11-29 19:10:10 +0000 (Mon, 29 Nov 2004) New Revision: 415 Modified: trunk/SQLObject/tests/test.py Log: Separated main() to use it in other test modules. Used getopt to parse options and arguments. Modified: trunk/SQLObject/tests/test.py =================================================================== --- trunk/SQLObject/tests/test.py 2004-11-29 16:07:00 UTC (rev 414) +++ trunk/SQLObject/tests/test.py 2004-11-29 19:10:10 UTC (rev 415) @@ -1,5 +1,5 @@ """ -Main (um, only) unit testing for SQLObject. +Main unit testing for SQLObject. Use -vv to see SQL queries, -vvv to also see output from queries, and together with --inserts to see the SQL from the standard @@ -8,7 +8,7 @@ from __future__ import generators -import sys +import sys, os if '--coverage' in sys.argv: import coverage print 'Starting coverage' @@ -1330,42 +1330,54 @@ return False return True -if __name__ == '__main__': - import unittest, sys, os + +def main(): + import unittest + from getopt import getopt, GetoptError + + try: + options, arguments = getopt(sys.argv[1:], "d:v", + ["database=", "extra-verbose", "super-verbose", + "inserts", "coverage"]) + except GetoptError: + usage(1) + dbs = [] newArgs = [] doCoverage = False - for arg in sys.argv[1:]: - if arg.startswith('-d'): - dbs.append(arg[2:]) - continue - if arg.startswith('--database='): - dbs.append(arg[11:]) - continue - if arg in ('-vv', '--extra-verbose'): - SQLObjectTest.debugSQL = True - if arg in ('-vvv', '--super-verbose'): - SQLObjectTest.debugSQL = True - SQLObjectTest.debugOutput = True - newArgs.append('-vv') - continue - if arg in ('--inserts',): + verbose = 0 + + for option, value in options: + if option in ('-d', '--database'): + dbs.append(value) + elif option == '--inserts': SQLObjectTest.debugInserts = True - continue - if arg in ('--coverage',): + elif option == '--coverage': # Handled earlier, so we get better coverage doCoverage = True - continue - newArgs.append(arg) + elif option == '--extra-verbose': + verbose = 1 + elif option == '--super-verbose': + verbose = 2 + elif option == '-v': + verbose += 1 + + if verbose >= 1: + SQLObjectTest.debugSQL = True + if verbose >= 2: + SQLObjectTest.debugOutput = True + newArgs.append('-vv') + newArgs.extend(arguments) + sys.argv = [sys.argv[0]] + newArgs if not dbs: dbs = ['mysql'] if dbs == ['all']: dbs = supportedDatabases() for db in dbs: + print 'Testing %s' % db curr_db = db setDatabaseType(db) - print 'Testing %s' % db try: unittest.main() except SystemExit: @@ -1374,3 +1386,5 @@ coverage.stop() coverModules() +if __name__ == '__main__': + main() |
From: <sub...@co...> - 2004-11-29 16:07:03
|
Author: ianb Date: 2004-11-29 16:07:00 +0000 (Mon, 29 Nov 2004) New Revision: 414 Modified: trunk/SQLObject/sqlobject/main.py Log: Was invalidly converting values to database representation, then putting them in the Python-representation attribute. Per email Re: [SQLObject] .set() fromPython Modified: trunk/SQLObject/sqlobject/main.py =================================================================== --- trunk/SQLObject/sqlobject/main.py 2004-11-29 16:05:52 UTC (rev 413) +++ trunk/SQLObject/sqlobject/main.py 2004-11-29 16:07:00 UTC (rev 414) @@ -770,7 +770,7 @@ for name, value in kw.items(): fromPy = getattr(self, '_SO_fromPython_%s' % name, None) if fromPy: - kw[name] = value = fromPy(value, self._SO_validatorState) + kw[name] = dbValue = fromPy(value, self._SO_validatorState) setattr(self, instanceName(name), value) for name, value in extra.items(): setattr(self, name, value) |
From: <sub...@co...> - 2004-11-29 16:05:56
|
Author: ianb Date: 2004-11-29 16:05:52 +0000 (Mon, 29 Nov 2004) New Revision: 413 Modified: trunk/SQLObject/tests/test.py Log: ClassRegistryTest: Error string for duplicate classes changed IndexTest1: Postgres raises ProgrammingError instead of IntegrityError, sometimes; just catches either now. Modified: trunk/SQLObject/tests/test.py =================================================================== --- trunk/SQLObject/tests/test.py 2004-11-29 12:21:59 UTC (rev 412) +++ trunk/SQLObject/tests/test.py 2004-11-29 16:05:52 UTC (rev 413) @@ -34,7 +34,7 @@ class Duplicate(SQLObject): pass except ValueError, err: - self.assertEqual(str(err), "class Duplicate is already in the registry") + assert str(err).startswith("class Duplicate is already in the registry") else: self.fail("should have raised an error on duplicate class definition") @@ -1198,14 +1198,9 @@ n += 1 SOIndex1(name=name, number=n) mod = SOIndex1._connection.module - # Firebird doesn't throw an integrity error, unfortunately: - if mod.__name__.endswith('kinterbasdb'): - exc = mod.ProgrammingError - else: - exc = mod.IntegrityError try: SOIndex1(name='blah', number=0) - except exc: + except (mod.ProgrammingError, mod.IntegrityError): # expected pass else: |
From: <sub...@co...> - 2004-11-12 05:20:30
|
Author: ianb Date: 2004-11-11 09:33:25 -0500 (Thu, 11 Nov 2004) New Revision: 349 Added: trunk/SQLObject/docs/presentation-2004-11/ trunk/SQLObject/docs/presentation-2004-11/sqlobject-and-database-progr= amming.html trunk/SQLObject/docs/presentation-2004-11/ui/ trunk/SQLObject/docs/presentation-2004-11/ui/bodybg.gif trunk/SQLObject/docs/presentation-2004-11/ui/custom.css trunk/SQLObject/docs/presentation-2004-11/ui/framing.css trunk/SQLObject/docs/presentation-2004-11/ui/opera.css trunk/SQLObject/docs/presentation-2004-11/ui/pretty.css trunk/SQLObject/docs/presentation-2004-11/ui/print.css trunk/SQLObject/docs/presentation-2004-11/ui/s5-core.css trunk/SQLObject/docs/presentation-2004-11/ui/slides.css trunk/SQLObject/docs/presentation-2004-11/ui/slides.js Log: A presentation I'm giving Added: trunk/SQLObject/docs/presentation-2004-11/sqlobject-and-database-p= rogramming.html =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/SQLObject/docs/presentation-2004-11/sqlobject-and-database-prog= ramming.html 2004-11-11 14:12:55 UTC (rev 348) +++ trunk/SQLObject/docs/presentation-2004-11/sqlobject-and-database-prog= ramming.html 2004-11-11 14:33:25 UTC (rev 349) @@ -0,0 +1,769 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"=20 + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> + +<html xmlns=3D"http://www.w3.org/1999/xhtml"> + +<head> +<title>SQLObject and Database Programming in Python</title> +<meta name=3D"version" content=3D"S5 1.0" /> +<link rel=3D"stylesheet" href=3D"ui/slides.css" type=3D"text/css" media=3D= "projection" id=3D"slideProj" /> +<link rel=3D"stylesheet" href=3D"ui/opera.css" type=3D"text/css" media=3D= "projection" id=3D"operaFix" /> +<link rel=3D"stylesheet" href=3D"ui/print.css" type=3D"text/css" +media=3D"print" id=3D"slidePrint" /> +<link rel=3D"stylesheet" href=3D"ui/custom.css" type=3D"text/css"/> +<sc ript src=3D"ui/slides.js" type=3D"text/javascript"></script> +</head> +<body> + +<div class=3D"layout"> + +<div id=3D"currentSlide"></div> +<div id=3D"header"></div> +<div id=3D"footer"> +<h1>ChiPy, 11 November 2004</h1> +<h2>SQLObject and Database Programming in Python</h2> +<div id=3D"controls"></div> +</div> + +</div> + + +<div class=3D"presentation"> + +<div class=3D"slide"> +<h1>SQLObject and Database Programming in Python</h1> +<h3>Ian Bicking</h3> +<h4>Imaginary Landscape</h4> +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + +<div class=3D"slide"> +<h1>The Ongoing Example</h1> + +<h2>Address book:</h2> +<p> +<ul> + <li><code>person</code>: + <ul> + <li><code>first_name</code></li> + <li><code>last_name</code></li> + <li><code>email</code> (many emails for one person)</li> + </ul></li> + <li><code>email</code>: + <ul> + <li><code>type</code></li> + <li><code>address</code></li> + </ul></li> +</ul> +</p> +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + +<div class=3D"slide"> +<h1>Database Programming in Python</h1> + +<p>Database programming in Python is based on the <b><a +href=3D"http://www.python.org/peps/pep-0249.html">DB-API</a></b>. +Some supported databases: + +<ul> +<li>MySQL</li> +<li>PostgreSQL (multiple drivers)</li> +<li>SQLite</li> +<li>Oracle</li> +<li>Sybase</li> +<li>Firebird (Interbase)</li> +<li>MAXDB (SAP DB)</li> +<li>MS SQL Server</li> +</p> +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + +<div class=3D"slide"> +<h1>DB API Example</h1> + +<p> +Example: + +<pre> +import MySQLdb +conn =3D MySQLdb.connect( + db=3D<span class=3D"string">'test'</span>, username=3D<span class=3D= "string">'ianb'</span>) +cur =3D conn.cursor() +cur.execute( + <span class=3D"sql">"SELECT id, first_name, last_name FROM person"</= span>) +result =3D cur.fetchall() +for id, first_name, last_name in result: + print <span class=3D"string">"%2i %s, %s"</span> % (id, last_name, f= irst_name) +</pre> + +</p> +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + +<div class=3D"slide"> +<h1>DB API</h1> + +<ul> +<li> Import the databse module (<code>MySQLdb</code>, +<code>psycopg</code>, <code>sqlite</code>, etc) </li> +<li> Use <code>module.connect(...)</code> to create a +connection. </li> +<li> Use <code>connection.cursor()</code> to get a cursor. Cursors do +all the work. </li> +<li> Use <code>cursor.execute(sql_query)</code> to run +something. </li> +<li> Use <code>cursor.fetchall()</code> to get results. </li>=20 + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + +<div class=3D"slide"> +<h1>DB API problems</h1> + +<ol> +<li> Connections and cursors are tedious. </li> +<li> The databases don't all work the same; multiple SQL +dialects. </li> +<li> Lots of time spent writing SQL. </li> +<li> Database updates can effect all the SQL you've written. </li> +<li> Unless you make abstractions... </li> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + +<div class=3D"slide"> +<h1>Abstractions</h1> + +<p> +<ul> +<li> Every significant database application has a database abstraction +layer. </li> +<li> You write one whether you mean to or not. </li> +</p> + +</div> + + + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + +<div class=3D"slide"> +<h1>Object-Relational Mappers</h1> + +<p> + +The table: + +<table border=3D1> + +<tr><th colspan=3D3>person</th></tr> + +<tr> + <th>id</th> + <th>first_name</th> + <th>last_name</th> +</tr> + +<tr> + <td>1</td> + <td>John</td> + <td>Doe</td> +</tr> +<tr> + <td>2</td> + <td>Tom</td> + <td>Jones</td> +</tr> +<tr> + <td colspan=3D3 align=3Dcenter>...</td> +</tr> +</table> + +<p> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + +<div class=3D"slide"> + +<h1>Object-Relational Mappers: Classes</h1> + +<p> + +The class: + +<table border=3D1> + +<tr class=3D"code-accent"><th colspan=3D3>person</th></tr> + +<tr class=3D"code-accent"> + <th>id</th> + <th>first_name</th> + <th>last_name</th> +</tr> + +<tr> + <td>1</td> + <td>John</td> + <td>Doe</td> +</tr> +<tr> + <td>2</td> + <td>Tom</td> + <td>Jones</td> +</tr> +<tr> + <td colspan=3D3 align=3Dcenter>...</td> +</tr> + +</table> +<p> + +<pre> +class Person(SQLObject): + <span class=3D"comment"># id is implicit</span> + first_name =3D StringCol() + last_name =3D StringCol() +</pre> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + + +<div class=3D"slide"> + +<h1>Object-Relational Mappers: Instances</h1> + +<p> + +An instance: + +<table border=3D1> + +<tr><th colspan=3D3>person</th></tr> + +<tr> + <th>id</th> + <th>first_name</th> + <th>last_name</th> +</tr> + +<tr class=3D"code-accent"> + <td>1</td> + <td>John</td> + <td>Doe</td> +</tr> +<tr> + <td>2</td> + <td>Tom</td> + <td>Jones</td> +</tr> +<tr> + <td colspan=3D3 align=3Dcenter>...</td> +</tr> + +</table> +<p> + +<pre> +john =3D Person.get(1) +assert john.first_name =3D=3D <span class=3D"string">'John'</span> +</pre> + + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + + +<div class=3D"slide"> +<h1>Object-Relational Mapping: summary</h1> + +<ul> + <li>Every table is a class</li> + <li>Every row is an instance</li> + <li>Columns become attributes</li> +</ul> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + + +<div class=3D"slide"> +<h1>Usage</h1> + +<pre> +__connection__ =3D <span class=3D"string">'postgres://pgsql@localhost/te= st'</span> +class Person(SQLObject): + first_name =3D StringCol() + last_name =3D StringCol() + emails =3D MultipleJoin('Email') +class Email(SQLObject): + person =3D ForeignKey(<span class=3D"string">'Person'</span>) + type =3D EnumCol(['home', 'work']) + address =3D StringCol() +def createTables(): + for table in (Person, Email): + table.createTable(ifNotExists=3DTrue) +</pre> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + +<div class=3D"slide"> +<h1>More use...</h1> + +Using your classes: + +<pre> +<span class=3D"prompt">>>></span> john =3D Person(first_name=3D= <span class=3D"string">'John'</span>, last_name=3D<span class=3D"string">= 'Doe'</span>) +<span class=3D"prompt">>>></span> email =3D Email(person=3Djohn= , type=3D<span class=3D"string">'home'</span>,=20 +<span class=3D"prompt">...</span> address=3D<span class=3D"string">'= jo...@wo...'</span>) +<span class=3D"prompt">>>></span> john.emails +<span class=3D"output">[]</span> +<span class=3D"prompt">>>></span> tom =3D Person(first_name=3D<= span class=3D"string">'Tom'</span>, last_name=3D<span class=3D"string">'J= ones'</span>) +<span class=3D"prompt">>>></span> tom is Person.get(tom.id) +<span class=3D"output">True</span> +<span class=3D"prompt">>>></span> list(Person.selectBy(first_na= me=3D<span class=3D"string">'John'</span>)) +<span class=3D"output">[]</span> +</pre> + +</div> + + + + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + + +<div class=3D"slide"> +<h1>Defining Classes</h1> + +<ul> + <li> The class name matches the table name </li> + <li> The attributes match the column names </li> + <li> (kind of...) </li> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + + +<div class=3D"slide"> +<h1>Defining Classes...</h1> + +<p> +You can add extra methods: + +<pre> +class Person(SQLObject): + ... + def _get_name(self): + return self.first_name + ' ' + self.last_name +</pre> + + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + +<div class=3D"slide"> +<h1></h1> + +<ul> +<li> <code>_get_attr()</code> methods are called whenever the +<code>obj.attr</code> attribute is called </li> +<li> <code>_set_attr()</code> methods are called whenever the +<code>obj.attr =3D value</code> statement is run </li> +<li> <code>_set_attr()</code> is optional </li> +</ul> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + + +<div class=3D"slide"> +<h1>Defining classes...</h1> + +<p> +You can override columns: + +<pre> +class Person(SQLObject): + ... + last_name_searchable =3D StringCol() + def _set_last_name(self, value): + self._SO_set_last_name(self, value) + self.last_name_lower =3D re.sub(<span class=3D"string">r'[^a-zA-= Z]'</span>, <span class=3D"string">''</span>, value).lower() +</pre> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + +<div class=3D"slide"> +<h1>Defining classes...</h1> + +<p> +You can fiddle with the naming: + +<pre> +class Person(SQLObject): + _table =3D <span class=3D"string">"people"</span> + first_name =3D StringCol(dbName=3D<span class=3D"string">"fname"</sp= an>) + ... +</pre> + +</p> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + +<div class=3D"slide"> +<h1>Connecting classes</h1> + +<p> +Foreign Keys: + +<pre> +class Email(SQLObject): + person =3D ForeignKey(<span class=3D"string">'Person'</span>) + ... +</pre> + +Note we use a string for <code class=3D"string">'Person'</code>. + + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + + +<div class=3D"slide"> +<h1>Backreferences</h1> + +<p> +The other side of one-to-many: + +<pre> +class Person(SQLObject): + ... + emails =3D MultipleJoin(<span class=3D"string">'Email'</span>) +</pre> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + +<div class=3D"slide"> +<h1>Many-to-many</h1> + +<p> +Many to many relationships imply a "hidden" table: + +<pre> +class Address(SQLObject): + people =3D RelatedJoin(<span class=3D"string">'Person'</span>) + ... +class Person(SQLObject): + addresses =3D RelatedJoin(<span class=3D"string">'Address'</span>) +</pre> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + +<div class=3D"slide"> +<h1>Lazy classes</h1> + +<p> +SQLObject can use reflection to figure out what columns your table +has: + +<pre> +class Person(SQLObject): + _fromDatabase =3D True +</pre> + +</p> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + + +<div class=3D"slide"> +<h1>Many-to-many...</h1> + +<p> +The intermediate table created: + +<pre> +CREATE TABLE address_person ( + address_id INT NOT NULL, + person_id INT NOT NULL +); +</pre> +</p> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + + +<div class=3D"slide"> +<h1>Instances</h1> + +<ul> +<li>Use <code>Person.get(id)</code> to retrieve a row</li> +<li>Use <code>Person(first_name=3D...)</code> to insert a row (the=20 +new object is returned)</li> +<li>Use <code>aPerson.destroySelf()</code> to delete a row</li> +</ul> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + +<div class=3D"slide"> +<h1>Updating</h1> + +<ul> +<li>Use <code>aPerson.first_name =3D <span +class=3D"string">"new_name"</span></code></li> +<li>Use <code>aPerson.set(first_name=3D<span +class=3D"string">"new_fname"</span>, last_name=3D<span +class=3D"string">"new_lname"</span>)</code> for multiple simultaneous +updates. </li> +</ul> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + + +<div class=3D"slide"> +<h1>Selecting</h1> + +<p> +You can use Python expressions (kind of): + +<pre> +query =3D Person.q.first_name =3D=3D <span class=3D"string">"Joe"</span> +Person.select(query) +</pre> + +</p> + + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + + +<div class=3D"slide"> +<h1>Selecting...</h1> + +<p> + +<ul> +<li><code>Class.<b>q</b>.<i>column_name</i></code> creates a magical +object that creates queries.</li> +<li><code>sqlrepr()</code> turns these query objects into SQL (mostly +hidden)</li> +<li><code>sqlrepr(Person.q.first_name =3D=3D <span +class=3D"string">"Joe"</span>, <span class=3D"string">'mysql')</code> +creates the SQL <code class=3D"sql">person.first_name =3D +'Joe'</code></li> + +</p> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + + +<div class=3D"slide"> +<h1>Selecting...</h1> + +<p> +Complicated joins are possible: + +<pre> +Person.select((Person.q.id =3D=3D Email.q.personID) + & (Email.q.address.startswith('joe'))) +</pre> + +Becomes: + +<pre> +SELECT person.id, person.first_name, person.last_name +FROM person, email +WHERE person.id =3D email.person_id + AND email.address LIKE 'joe%' +</pre> + +</p> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + +<div class=3D"slide"> +<h1>SelectResult</h1> + +Select results are fancy. + +<ul> +<li> You can slice them, and <code class=3D"sql">LIMIT</code> and <code +class=3D"sql">OFFSET</code> statements are added to your query</li> +<li> You can iterate through them, and avoid loading all objects into +memory</li> +<li> You can do things like <code>select_result.count()</code> to run +aggregate functions </li> +<li> To get a real list, you must do <code>list(select_result)</code></l= i> + +</ul> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + + +<div class=3D"slide"> +<h1>Caching</h1> + +<ul> +<li>SQLObject caches everything</li> +<li>Maybe too much (bad if you have multiple updaters)</li> +<li>Needs to because there's no joins</li> +<li>Has potential to be faster than ad hoc queries</li> +<li>Thread safety or thread confusion?</li> +</ul> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + +<div class=3D"slide"> +<h1>Performance</h1> + +<ul> +<li> Not good if you are dealing with lots and lots of rows </li> +<li> Inserts and selects substantially slower </li> +<li> But for many applications you'll pay that price sooner or later +anyway </li> +</ul> + +</div> + + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + + +<div class=3D"slide"> +<h1>Solving the question by avoidance</h1> + +<ul> + <li> SQLObject doesn't let you forget SQL </li> + <li> You still have to think relationally </li> + <li> But many relational concepts don't work either </li> + <li> Advantage: it's easy </li> +</ul> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + + +<div class=3D"slide"> +<h1>ORM Mismatch</h1> + +<ul> + <li> Python classes aren't tables</li> + <li> Python instances aren't rows</li> + <li> Primitive values (like <code class=3D"string">"John"</code>) + aren't part of the ORM at all </li> +<ul> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + + +<div class=3D"slide"> +<h1>ORM Mismatch: Classes</h1> + +<ul> + <li> Databases don't have inheritance </li> + <li> Even when they do, no one uses it </li> + <li> Some tables are too boring (e.g., intermediate tables for + many-to-many relationships) </li> +</ul> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + +<div class=3D"slide"> +<h1>ORM Mismatch: Instances</h1> + +<ul> + <li> Instances get garbage collected, rows are forever </li> + <li> Instances can't be pre-allocated </li> + <li> In the relational calculus, rows don't have real identity </li> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + + +<div class=3D"slide"> +<h1>ORM Mismatch: Relations</h1> + +<ul> + <li> Things like joins don't make sense for objects, but are central + to relations </li> + <li> Views and stored procedures also don't make sense </li> +</ul> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + + +<div class=3D"slide"> +<h1>Solving the Mismatch</h1> + +SQLObject's answer: don't try too hard. + +<ul> + <li> There exist database restrictions; not every database schema maps + well. (But most are fine) </li> + <li> There exist object restrictions; not every object-oriented + concept maps well; e.g., no inheritance. </li> +</ul> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + + + +</div> + +</body> +</html> Added: trunk/SQLObject/docs/presentation-2004-11/ui/bodybg.gif =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D (Binary files differ) Property changes on: trunk/SQLObject/docs/presentation-2004-11/ui/bodybg.= gif ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: trunk/SQLObject/docs/presentation-2004-11/ui/custom.css =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/SQLObject/docs/presentation-2004-11/ui/custom.css 2004-11-11 14= :12:55 UTC (rev 348) +++ trunk/SQLObject/docs/presentation-2004-11/ui/custom.css 2004-11-11 14= :33:25 UTC (rev 349) @@ -0,0 +1,39 @@ +.string {=20 + color: #006600; +} + +.comment {=20 + color: #993300; +} + +.output {=20 + font-weight: bold; + color: #000066; +} + +.prompt {=20 + color: #000000; +} + +.sql {=20 + color: #006600; + font-weight: bold; +} + +code {=20 + color: #000066; + padding-left: 3px; + padding-right: 3px; + font-weight: bold; +} + +pre {=20 + margin-left: 7px; + padding: 3px; + border-left: 4px solid #666666; + background-color: #eeeeee; +} + +tr.code-accent td, tr.code-accent th {=20 + border: medium green dashed; +} \ No newline at end of file Added: trunk/SQLObject/docs/presentation-2004-11/ui/framing.css =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/SQLObject/docs/presentation-2004-11/ui/framing.css 2004-11-11 1= 4:12:55 UTC (rev 348) +++ trunk/SQLObject/docs/presentation-2004-11/ui/framing.css 2004-11-11 1= 4:33:25 UTC (rev 349) @@ -0,0 +1,23 @@ +/* The following styles size, place, and layer the slide components. + Edit these if you want to change the overall slide layout. + The commented lines can be uncommented (and modified, if necessary)=20 + to help you with the rearrangement process. */ + +div#header, div#footer, div.slide {width: 100%; top: 0; left: 0;} +div#header {top: 0; height: 3em; z-index: 1;} +div#footer {top: auto; bottom: 0; height: 2.5em; z-index: 5;} +div.slide {top: 0; width: 92%; padding: 3.5em 4% 4%; z-index: 2;} +div#controls {left: 50%; top: 0; width: 50%; height: 100%; z-index: 1;} +#footer>div#controls {bottom: 0; top: auto; height: auto;} + +div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; + margin: 0;} +#currentSlide {position: absolute; width: 10%; left: 45%; bottom: 1em; z= -index: 10;} +html>body #currentSlide {position: fixed;} + +/* +div#header {background: #FCC;} +div#footer {background: #CCF;} +div#controls {background: #BBD;} +div#currentSlide {background: #FFC;} +*/ Added: trunk/SQLObject/docs/presentation-2004-11/ui/opera.css =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/SQLObject/docs/presentation-2004-11/ui/opera.css 2004-11-11 14:= 12:55 UTC (rev 348) +++ trunk/SQLObject/docs/presentation-2004-11/ui/opera.css 2004-11-11 14:= 33:25 UTC (rev 349) @@ -0,0 +1,7 @@ +/* DO NOT CHANGE THESE unless you really want to break Opera Show */ +div.slide { + visibility: visible !important; + position: static !important; + page-break-before: always; +} +#slide0 {page-break-before: avoid;} Added: trunk/SQLObject/docs/presentation-2004-11/ui/pretty.css =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/SQLObject/docs/presentation-2004-11/ui/pretty.css 2004-11-11 14= :12:55 UTC (rev 348) +++ trunk/SQLObject/docs/presentation-2004-11/ui/pretty.css 2004-11-11 14= :33:25 UTC (rev 349) @@ -0,0 +1,74 @@ +/* Following are the presentation styles -- edit away! + Note that the 'body' font size may have to be changed if the resoluti= on is + different than expected. */ + +body {background: #fff url(bodybg.gif) -16px 0 no-repeat; color: #000; f= ont-size: 2em;} +:link, :visited {text-decoration: none;} +#controls :active {color: #88A !important;} +#controls :focus {outline: 1px dotted #227;} +h1, h2, h3, h4 {font-size: 100%; margin: 0; padding: 0; font-weight: inh= erit;} +ul, pre {margin: 0; line-height: 1em;} +html, body {margin: 0; padding: 0;} + +blockquote, q {font-style: italic;} +blockquote {padding: 0 2em 0.5em; margin: 0 1.5em 0.5em; text-align: cen= ter; font-size: 1em;} +blockquote p {margin: 0;} +blockquote i {font-style: normal;} +blockquote b {display: block; margin-top: 0.5em; font-weight: normal; fo= nt-size: smaller; font-style: normal;} +blockquote b i {font-style: italic;} + +kbd {font-weight: bold; font-size: 1em;} +sup {font-size: smaller; line-height: 1px;} + +code {padding: 2px 0.25em; font-weight: bold; color: #533;} +code.bad, code del {color: red;} +code.old {color: silver;} +pre {padding: 0; margin: 0.25em 0 0.5em 0.5em; color: #533; font-size: 9= 0%;} +pre code {display: block;} +ul {margin-left: 5%; margin-right: 7%; list-style: disc;} +li {margin-top: 0.75em; margin-right: 0;} +ul ul {line-height: 1;} +ul ul li {margin: .2em; font-size: 85%; list-style: square;} +img.leader {display: block; margin: 0 auto;} + +div#header, div#footer {background: #005; color: #AAB; + font-family: Verdana, Helvetica, sans-serif;} +div#header {background: #005 url(bodybg.gif) -16px 0 no-repeat; + line-height: 1px;} +div#footer {font-size: 0.5em; font-weight: bold; padding: 1em 0;} +#footer h1, #footer h2 {display: block; padding: 0 1em;} +#footer h2 {font-style: italic;} + +div.long {font-size: 0.75em;} +.slide h1 {position: absolute; top: 0.7em; left: 87px; z-index: 1; + margin: 0; padding: 0.3em 0 0 50px; white-space: nowrap; + font: bold 150%/1em Helvetica, sans-serif; text-transform: capitalize; + color: #DDE; background: #005;} +.slide h3 {font-size: 130%;} +h1 abbr {font-variant: small-caps;} + +div#controls {position: absolute; z-index: 1; left: 50%; top: 0; + width: 50%; height: 100%; + text-align: right;} +#footer>div#controls {position: fixed; bottom: 0; padding: 1em 0; + top: auto; height: auto;} +div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; + margin: 0; padding: 0;} +div#controls a {font-size: 2em; padding: 0; margin: 0 0.5em;=20 + background: #005; border: none; color: #779;=20 + cursor: pointer;} +div#controls select {visibility: hidden; background: #DDD; color: #227;} +div#controls div:hover select {visibility: visible;} + +#currentSlide {text-align: center; font-size: 0.5em; color: #449;} + +#slide0 {padding-top: 3.5em; font-size: 90%;} +#slide0 h1 {position: static; margin: 1em 0 1.33em; padding: 0; + font: bold 2em Helvetica, sans-serif; white-space: normal; + color: #000; background: transparent;} +#slide0 h3 {margin-top: 0.5em; font-size: 1.5em;} +#slide0 h4 {margin-top: 0; font-size: 1em;} + +ul.urls {list-style: none; display: inline; margin: 0;} +.urls li {display: inline; margin: 0;} +.note {display: none;} Added: trunk/SQLObject/docs/presentation-2004-11/ui/print.css =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/SQLObject/docs/presentation-2004-11/ui/print.css 2004-11-11 14:= 12:55 UTC (rev 348) +++ trunk/SQLObject/docs/presentation-2004-11/ui/print.css 2004-11-11 14:= 33:25 UTC (rev 349) @@ -0,0 +1 @@ +/* The next rule is necessary to have all slides appear in print! DO NOT= REMOVE IT! */=0Ddiv.slide, ul {page-break-inside: avoid; visibility: vis= ible !important;}=0Dh1 {page-break-after: avoid;}=0D=0Dbody {font-size: 1= 2pt; background: white;}=0D* {color: black;}=0D=0D#slide0 h1 {font-size: = 200%; border: none; margin: 0.5em 0 0.25em;}=0D#slide0 h3 {margin: 0; pad= ding: 0;}=0D#slide0 h4 {margin: 0 0 0.5em; padding: 0;}=0D#slide0 {margin= -bottom: 3em;}=0D=0Dh1 {border-top: 2pt solid gray; border-bottom: 1px do= tted silver;}=0D.extra {background: transparent !important;}=0Ddiv.extra,= pre.extra, .example {font-size: 10pt; color: #333;}=0Dul.extra a {font-w= eight: bold;}=0Dp.example {display: none;}=0D=0D#header {display: none;}=0D= #footer h1 {margin: 0; border-bottom: 1px solid; color: gray; font-style:= italic;}=0D#footer h2, #controls {display: none;}=0D=0D#currentSlide {di= splay: none;} \ No newline at end of file Added: trunk/SQLObject/docs/presentation-2004-11/ui/s5-core.css =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/SQLObject/docs/presentation-2004-11/ui/s5-core.css 2004-11-11 1= 4:12:55 UTC (rev 348) +++ trunk/SQLObject/docs/presentation-2004-11/ui/s5-core.css 2004-11-11 1= 4:33:25 UTC (rev 349) @@ -0,0 +1,9 @@ +/* Do not edit or override these styles! The system will likely break if= you do. */ + +div#header, div#footer, div.slide {position: absolute;} +html>body div#header, html>body div#footer, html>body div.slide {positio= n: fixed;} +div.slide { visibility: hidden;} +#slide0 {visibility: visible;} +div#controls {position: absolute;} +#footer>div#controls {position: fixed;} +.handout {display: none;} Added: trunk/SQLObject/docs/presentation-2004-11/ui/slides.css =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/SQLObject/docs/presentation-2004-11/ui/slides.css 2004-11-11 14= :12:55 UTC (rev 348) +++ trunk/SQLObject/docs/presentation-2004-11/ui/slides.css 2004-11-11 14= :33:25 UTC (rev 349) @@ -0,0 +1,3 @@ +@import url(s5-core.css); /* required to make the slide show run at all = */ +@import url(framing.css); /* sets basic placement and size of slide comp= onents */ +@import url(pretty.css); /* stuff that makes the slides look better tha= n blah */ Added: trunk/SQLObject/docs/presentation-2004-11/ui/slides.js =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/SQLObject/docs/presentation-2004-11/ui/slides.js 2004-11-11 14:= 12:55 UTC (rev 348) +++ trunk/SQLObject/docs/presentation-2004-11/ui/slides.js 2004-11-11 14:= 33:25 UTC (rev 349) @@ -0,0 +1,256 @@ +// S5 slides.js -- released under CC by-sa 2.0 license +// +// Please see http://www.meyerweb.com/eric/tools/s5/credits.html for inf= ormation=20 +// about all the wonderful and talented contributors to this code! + +var snum =3D 0; +var smax =3D 1; +var undef; +var slcss =3D 1; +var isIE =3D navigator.appName =3D=3D 'Microsoft Internet Explorer' ? 1 = : 0; +var isOp =3D navigator.userAgent.indexOf('Opera') > -1 ? 1 : 0; +var isGe =3D navigator.userAgent.indexOf('Gecko') > -1 && navigator.user= Agent.indexOf('Safari') < 1 ? 1 : 0; +var slideCSS =3D document.getElementById('slideProj').href; + +function isClass(object, className) { + return (object.className.search('(^|\\s)' + className + '(\\s|$)') !=3D= -1); +} + +function GetElementsWithClassName(elementName,className) { + var allElements =3D document.getElementsByTagName(elementName); + var elemColl =3D new Array(); + for (i =3D 0; i< allElements.length; i++) { + if (isClass(allElements[i], className)) { + elemColl[elemColl.length] =3D allElements[i]; + } + } + return elemColl; +} + +function isParentOrSelf(element, id) { + if (element =3D=3D null || element.nodeName=3D=3D'BODY') return false; + else if (element.id =3D=3D id) return true; + else return isParentOrSelf(element.parentNode, id); +} + +function nodeValue(node) { + var result =3D ""; + if (node.nodeType =3D=3D 1) { + var children =3D node.childNodes; + for ( i =3D 0; i < children.length; ++i ) { + result +=3D nodeValue(children[i]); + } =09 + } + else if (node.nodeType =3D=3D 3) { + result =3D node.nodeValue; + } + return(result); +} + +function slideLabel() { + var slideColl =3D GetElementsWithClassName('div','slide'); + var list =3D document.getElementById('jumplist'); + smax =3D slideColl.length; + for (n =3D 0; n < smax; n++) { + var obj =3D slideColl[n]; + + var did =3D 'slide' + n.toString(); + obj.setAttribute('id',did); + if(isOp) continue; + + var otext =3D ''; + var menu =3D obj.firstChild; + if (!menu) continue; // to cope with empty slides + while (menu && menu.nodeType =3D=3D 3) { + menu =3D menu.nextSibling; + } + if (!menu) continue; // to cope with slides with only text nodes + + var menunodes =3D menu.childNodes; + for (o =3D 0; o < menunodes.length; o++) { + otext +=3D nodeValue(menunodes[o]); + } + list.options[list.length] =3D new Option(n+' : ' +otext,n); + } +} + +function currentSlide() { + var cs; + if (document.getElementById) { + cs =3D document.getElementById('currentSlide'); + } else { + cs =3D document.currentSlide; + } + cs.innerHTML =3D '<span id=3D"csHere">' + snum + '<\/span> ' +=20 + '<span id=3D"csSep">\/<\/span> ' +=20 + '<span id=3D"csTotal">' + (smax-1) + '<\/span>'; + if (snum =3D=3D 0) { + cs.style.visibility =3D 'hidden'; + } else { + cs.style.visibility =3D 'visible'; + } +} + +function go(inc) { + if (document.getElementById("slideProj").disabled) return; + var cid =3D 'slide' + snum; + if (inc !=3D 'j') { + snum +=3D inc; + lmax =3D smax - 1; + if (snum > lmax) snum =3D 0; + if (snum < 0) snum =3D lmax; + } else { + snum =3D parseInt(document.getElementById('jumplist').value); + } + var nid =3D 'slide' + snum; + var ne =3D document.getElementById(nid); + if (!ne) { + ne =3D document.getElementById('slide0'); + snum =3D 0; + } + document.getElementById(cid).style.visibility =3D 'hidden'; + ne.style.visibility =3D 'visible'; + document.getElementById('jumplist').selectedIndex =3D snum; + currentSlide(); +} + +function toggle() { + var slideColl =3D GetElementsWithClassName('div','slide'); + var obj =3D document.getElementById('slideProj'); + if (!obj.disabled) { + obj.disabled =3D true; + for (n =3D 0; n < smax; n++) { + var slide =3D slideColl[n]; + slide.style.visibility =3D 'visible'; + } + } else { + obj.disabled =3D false; + for (n =3D 0; n < smax; n++) { + var slide =3D slideColl[n]; + slide.style.visibility =3D 'hidden'; + } + slideColl[snum].style.visibility =3D 'visible'; + } +} + +function showHide(action) { + var obj =3D document.getElementById('jumplist'); + switch (action) { + case 's': obj.style.visibility =3D 'visible'; break; + case 'h': obj.style.visibility =3D 'hidden'; break; + case 'k': + if (obj.style.visibility !=3D 'visible') { + obj.style.visibility =3D 'visible'; + } else { + obj.style.visibility =3D 'hidden'; + } + break; + } +} + +// 'keys' code adapted from MozPoint (http://mozpoint.mozdev.org/) +function keys(key) { + if (!key) { + key =3D event; + key.which =3D key.keyCode; + } + switch (key.which) { + case 10: // return + case 13: // enter + if (window.event && isParentOrSelf(window.event.srcElement, "controls= ")) return; + if (key.target && isParentOrSelf(key.target, "controls")) return; + case 32: // spacebar + case 34: // page down + case 39: // rightkey + case 40: // downkey + go(1); + break; + case 33: // page up + case 37: // leftkey + case 38: // upkey + go(-1); + break; + case 84: // t + toggle(); + break; + case 67: // c + showHide('k'); + break; + } +} + +function clicker(e) { + var target; + if (window.event) { + target =3D window.event.srcElement; + e =3D window.event; + } else target =3D e.target; + if (target.href !=3D null || isParentOrSelf(target, 'controls')) retur= n true; + if (!e.which || e.which =3D=3D 1) go(1); +} + +function slideJump() { + if (window.location.hash =3D=3D null) return; + var sregex =3D /^#slide(\d+)$/; + var matches =3D sregex.exec(window.location.hash); + var dest =3D null; + if (matches !=3D null) { + dest =3D parseInt(matches[1]); + } else { + var target =3D window.location.hash.slice(1); + var targetElement =3D null; + var aelements =3D document.getElementsByTagName("a"); + for (i =3D 0; i < aelements.length; i++) { + var aelement =3D aelements[i]; + if ( (aelement.name && aelement.name =3D=3D target) + || (aelement.id && aelement.id =3D=3D target) ) { + targetElement =3D aelement; + break; + } + } + while(targetElement !=3D null && targetElement.nodeName !=3D "body") { + if (targetElement.className =3D=3D "slide") break; + targetElement =3D targetElement.parentNode; + } + if (targetElement !=3D null && targetElement.className =3D=3D "slide")= { + dest =3D parseInt(targetElement.id.slice(1)); + } + } + if (dest !=3D null) + go(dest - snum); + } +=20 +function createControls() { + controlsDiv =3D document.getElementById("controls"); + if (!controlsDiv) return; + controlsDiv.innerHTML =3D '<form action=3D"#" id=3D"controlForm">' + + '<div>' + + '<a accesskey=3D"t" id=3D"toggle" href=3D"javascript:toggle();">Ø<= \/a>' + + '<a accesskey=3D"z" id=3D"prev" href=3D"javascript:go(-1);">«<\/a= >' + + '<a accesskey=3D"x" id=3D"next" href=3D"javascript:go(1);">»<\/a>= ' + + '<\/div>' + + '<div onmouseover=3D"showHide(\'s\');" onmouseout=3D"showHide(\'h\');">= <select id=3D"jumplist" onchange=3D"go(\'j\');"><\/select><\/div>' + + '<\/form>'; +} + +function notOperaFix() { + var obj =3D document.getElementById('slideProj'); + obj.setAttribute('media','screen'); + if (isGe) { + obj.setAttribute('href','null'); // Gecko fix + obj.setAttribute('href',slideCSS); // Gecko fix + } +} + +function startup() { + if (!isOp) createControls(); + slideLabel(); + if (!isOp) { =09 + notOperaFix(); + slideJump(); + document.onkeyup =3D keys; + document.onclick =3D clicker; + } +} + +window.onload =3D startup; |
From: <sub...@co...> - 2004-11-12 04:47:12
|
Author: ianb Date: 2004-11-11 13:57:53 -0500 (Thu, 11 Nov 2004) New Revision: 364 Modified: trunk/SQLObject/docs/presentation-2004-11/sqlobject-and-database-progr= amming.html trunk/SQLObject/docs/presentation-2004-11/ui/custom.css trunk/SQLObject/docs/presentation-2004-11/ui/framing.css trunk/SQLObject/docs/presentation-2004-11/ui/pretty.css Log: Edits and visual updates Modified: trunk/SQLObject/docs/presentation-2004-11/sqlobject-and-databas= e-programming.html =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/SQLObject/docs/presentation-2004-11/sqlobject-and-database-prog= ramming.html 2004-11-11 18:38:24 UTC (rev 363) +++ trunk/SQLObject/docs/presentation-2004-11/sqlobject-and-database-prog= ramming.html 2004-11-11 18:57:53 UTC (rev 364) @@ -11,7 +11,7 @@ <link rel=3D"stylesheet" href=3D"ui/print.css" type=3D"text/css" media=3D"print" id=3D"slidePrint" /> <link rel=3D"stylesheet" href=3D"ui/custom.css" type=3D"text/css"/> -<sc ript src=3D"ui/slides.js" type=3D"text/javascript"></script> +<script src=3D"ui/slides.js" type=3D"text/javascript"></script> </head> <body> =20 @@ -62,7 +62,7 @@ <!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> =20 <div class=3D"slide"> -<h1>Database Programming in Python</h1> +<h1>The DB API</h1> =20 <p>Database programming in Python is based on the <b><a href=3D"http://www.python.org/peps/pep-0249.html">DB-API</a></b>. @@ -72,11 +72,9 @@ <li>MySQL</li> <li>PostgreSQL (multiple drivers)</li> <li>SQLite</li> -<li>Oracle</li> -<li>Sybase</li> <li>Firebird (Interbase)</li> <li>MAXDB (SAP DB)</li> -<li>MS SQL Server</li> +<li>Oracle, Sybase, MS SQL Server</li> </p> </div> =20 @@ -109,7 +107,7 @@ <h1>DB API</h1> =20 <ul> -<li> Import the databse module (<code>MySQLdb</code>, +<li> Import the database module (<code>MySQLdb</code>, <code>psycopg</code>, <code>sqlite</code>, etc) </li> <li> Use <code>module.connect(...)</code> to create a connection. </li> @@ -194,12 +192,14 @@ =20 <div class=3D"slide"> =20 -<h1>Object-Relational Mappers: Classes</h1> +<h1>ORMs: Classes</h1> =20 <p> =20 The class: =20 +<table width=3D"100%"> +<tr><td> <table border=3D1> =20 <tr class=3D"code-accent"><th colspan=3D3>person</th></tr> @@ -225,14 +225,16 @@ </tr> =20 </table> -<p> +</td> =20 +<td> <pre> class Person(SQLObject): <span class=3D"comment"># id is implicit</span> first_name =3D StringCol() last_name =3D StringCol() </pre> +</td></tr></table> =20 </div> =20 @@ -241,12 +243,15 @@ =20 <div class=3D"slide"> =20 -<h1>Object-Relational Mappers: Instances</h1> +<h1>ORMs: Instances</h1> =20 <p> =20 An instance: =20 +<table width=3D"100%"> +<tr><td> + <table border=3D1> =20 <tr><th colspan=3D3>person</th></tr> @@ -272,13 +277,17 @@ </tr> =20 </table> -<p> =20 +</td><td> + <pre> -john =3D Person.get(1) -assert john.first_name =3D=3D <span class=3D"string">'John'</span> +<span class=3D"prompt">>>></span> john =3D Person.get(1) +<span class=3D"prompt">>>></span> john.first_name +<span class=3D"output">'John'</span> </pre> =20 +</td></tr></table> +</p> =20 </div> =20 @@ -286,7 +295,7 @@ =20 =20 <div class=3D"slide"> -<h1>Object-Relational Mapping: summary</h1> +<h1>ORM: summary</h1> =20 <ul> <li>Every table is a class</li> @@ -308,10 +317,32 @@ first_name =3D StringCol() last_name =3D StringCol() emails =3D MultipleJoin('Email') +</pre> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + + +<div class=3D"slide"> +<h1>Usage (classes)</h1> + +<pre> class Email(SQLObject): person =3D ForeignKey(<span class=3D"string">'Person'</span>) type =3D EnumCol(['home', 'work']) address =3D StringCol() +</pre> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + + +<div class=3D"slide"> +<h1>Usage (creating tables)</h1> + +<pre> def createTables(): for table in (Person, Email): table.createTable(ifNotExists=3DTrue) @@ -322,7 +353,7 @@ <!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> =20 <div class=3D"slide"> -<h1>More use...</h1> +<h1>Usage (instances)</h1> =20 Using your classes: =20 @@ -332,6 +363,17 @@ <span class=3D"prompt">...</span> address=3D<span class=3D"string">'= jo...@wo...'</span>) <span class=3D"prompt">>>></span> john.emails <span class=3D"output">[]</span> +</pre> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + + +<div class=3D"slide"> +<h1>Usage (instances)</h1> + +<pre> <span class=3D"prompt">>>></span> tom =3D Person(first_name=3D<= span class=3D"string">'Tom'</span>, last_name=3D<span class=3D"string">'J= ones'</span>) <span class=3D"prompt">>>></span> tom is Person.get(tom.id) <span class=3D"output">True</span> @@ -342,8 +384,6 @@ </div> =20 =20 - - <!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> =20 =20 @@ -373,13 +413,17 @@ return self.first_name + ' ' + self.last_name </pre> =20 +<pre> +<span class=3D"prompt">>>></span> tom.name +<span class=3D"output">'Tom Jones'</span> +</pre> =20 </div> =20 <!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> =20 <div class=3D"slide"> -<h1></h1> +<h1>Automatic properties</h1> =20 <ul> <li> <code>_get_attr()</code> methods are called whenever the @@ -487,36 +531,38 @@ <!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> =20 <div class=3D"slide"> -<h1>Lazy classes</h1> +<h1>Many-to-many...</h1> =20 <p> -SQLObject can use reflection to figure out what columns your table -has: +The intermediate table created: =20 <pre> -class Person(SQLObject): - _fromDatabase =3D True +CREATE TABLE address_person ( + address_id INT NOT NULL, + person_id INT NOT NULL +); </pre> - </p> =20 </div> =20 <!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> =20 - <div class=3D"slide"> -<h1>Many-to-many...</h1> +<h1>Lazy classes</h1> =20 <p> -The intermediate table created: +SQLObject can use reflection to figure out what columns your table +has: =20 <pre> -CREATE TABLE address_person ( - address_id INT NOT NULL, - person_id INT NOT NULL -); +class Person(SQLObject): + _fromDatabase =3D True </pre> + +You can start with <code>_fromDatabase =3D True</code> and add=20 +in explicit columns, overriding defaults. + </p> =20 </div> @@ -524,6 +570,7 @@ <!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> =20 =20 + <div class=3D"slide"> <h1>Instances</h1> =20 @@ -542,12 +589,13 @@ <h1>Updating</h1> =20 <ul> -<li>Use <code>aPerson.first_name =3D <span -class=3D"string">"new_name"</span></code></li> -<li>Use <code>aPerson.set(first_name=3D<span +<li><code>aPerson.first_name =3D <span +class=3D"string">"new_name"</span></code><br> + <code class=3D"sql">UPDATE</code> is executed immediately </li> +<li> Update multiple columns at once: <br> +<code>aPerson.set(first_name=3D<span class=3D"string">"new_fname"</span>, last_name=3D<span -class=3D"string">"new_lname"</span>)</code> for multiple simultaneous -updates. </li> +class=3D"string">"new_lname"</span>)</code> </li> </ul> =20 </div> @@ -580,7 +628,7 @@ <p> =20 <ul> -<li><code>Class.<b>q</b>.<i>column_name</i></code> creates a magical +<li><code>Class.<b style=3D"color: #990000">q</b>.column_name</code> cre= ates a magical object that creates queries.</li> <li><code>sqlrepr()</code> turns these query objects into SQL (mostly hidden)</li> @@ -625,13 +673,13 @@ <div class=3D"slide"> <h1>SelectResult</h1> =20 -Select results are fancy. +Select results are more than just lists: =20 <ul> <li> You can slice them, and <code class=3D"sql">LIMIT</code> and <code class=3D"sql">OFFSET</code> statements are added to your query</li> <li> You can iterate through them, and avoid loading all objects into -memory</li> +memory </li> <li> You can do things like <code>select_result.count()</code> to run aggregate functions </li> <li> To get a real list, you must do <code>list(select_result)</code></l= i> @@ -673,16 +721,14 @@ =20 <!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> =20 - <div class=3D"slide"> -<h1>Solving the question by avoidance</h1> +<h1>The problem with ORMs</h1> =20 -<ul> - <li> SQLObject doesn't let you forget SQL </li> - <li> You still have to think relationally </li> - <li> But many relational concepts don't work either </li> - <li> Advantage: it's easy </li> -</ul> +<p> +ORMs live in the world between Object Oriented programmers (and +programs) and Relational programmers (and programs). Neither will be +happy. +</p> =20 </div> =20 @@ -695,8 +741,6 @@ <ul> <li> Python classes aren't tables</li> <li> Python instances aren't rows</li> - <li> Primitive values (like <code class=3D"string">"John"</code>) - aren't part of the ORM at all </li> <ul> =20 </div> @@ -711,7 +755,7 @@ <li> Databases don't have inheritance </li> <li> Even when they do, no one uses it </li> <li> Some tables are too boring (e.g., intermediate tables for - many-to-many relationships) </li> + many-to-many relationships), or don't warrant the overhead </li> </ul> =20 </div> @@ -723,7 +767,8 @@ =20 <ul> <li> Instances get garbage collected, rows are forever </li> - <li> Instances can't be pre-allocated </li> + <li> Rows exist even when instances don't </li> + <li> Instances may still exist when the row is gone </li> <li> In the relational calculus, rows don't have real identity </li> =20 </div> @@ -738,13 +783,29 @@ <li> Things like joins don't make sense for objects, but are central to relations </li> <li> Views and stored procedures also don't make sense </li> + <li> Aggregate functions and set operations aren't natural in Python=20 + (the <code>for</code> loop is natural) </li> </ul> =20 </div> =20 <!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> =20 +<div class=3D"slide"> +<h1>Solving the question by avoidance</h1> =20 +<ul> + <li> SQLObject doesn't let you forget SQL </li> + <li> You still have to think relationally </li> + <li> But many relational concepts don't work either </li> + <li> Advantage: it's easy </li> +</ul> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + + <div class=3D"slide"> <h1>Solving the Mismatch</h1> =20 @@ -753,17 +814,50 @@ <ul> <li> There exist database restrictions; not every database schema maps well. (But most are fine) </li> + <li> Sometimes you have to do things more slowly or more imperatively, + compared to a purely relational technique </li> <li> There exist object restrictions; not every object-oriented - concept maps well; e.g., no inheritance. </li> + concept maps well; e.g., no inheritance </li> + <li> Inheritance isn't planned; this isn't transparent persistence </li= > </ul> =20 </div> =20 <!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> =20 +<div class=3D"slide"> +<h1>Some other alternatives</h1> =20 +Several Python ORMs exist; contrasts:=20 +<ul> + <li> SQLObject doesn't try to be too OO, so it works well with normal=20 + database schemas </li> + <li> SQLObject doesn't have a compile step </li> + <li> SQLObject isn't <i>just</i> a SQL wrapper </li> + <li> SQLObject is reasonably complete </li> +</ul> =20 </div> =20 +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + + +<div class=3D"slide"> +<h1>Other alternatives...</h1> + +<p> + +See the Python Wiki at: <a href=3D"http://python.org/moin">python.org/mo= in</a><br> +The page is <a href=3D"http://python.org/moin/HigherLevelDatabaseProgram= ming">HigherLevelDatabaseProgramming</a>. +</p> + +</div> + +<!-- =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --> + + + +</div> + </body> </html> Modified: trunk/SQLObject/docs/presentation-2004-11/ui/custom.css =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/SQLObject/docs/presentation-2004-11/ui/custom.css 2004-11-11 18= :38:24 UTC (rev 363) +++ trunk/SQLObject/docs/presentation-2004-11/ui/custom.css 2004-11-11 18= :57:53 UTC (rev 364) @@ -1,3 +1,7 @@ +td { + vertical-align: top; +} + .string {=20 color: #006600; } @@ -16,7 +20,7 @@ } =20 .sql {=20 - color: #006600; + color: #004466; font-weight: bold; } =20 Modified: trunk/SQLObject/docs/presentation-2004-11/ui/framing.css =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/SQLObject/docs/presentation-2004-11/ui/framing.css 2004-11-11 1= 8:38:24 UTC (rev 363) +++ trunk/SQLObject/docs/presentation-2004-11/ui/framing.css 2004-11-11 1= 8:57:53 UTC (rev 364) @@ -4,15 +4,15 @@ to help you with the rearrangement process. */ =20 div#header, div#footer, div.slide {width: 100%; top: 0; left: 0;} -div#header {top: 0; height: 3em; z-index: 1;} -div#footer {top: auto; bottom: 0; height: 2.5em; z-index: 5;} +div#header {top: 0; height: 2em; z-index: 1;} +div#footer {top: auto; bottom: 0; height: 1.75em; z-index: 5;} div.slide {top: 0; width: 92%; padding: 3.5em 4% 4%; z-index: 2;} div#controls {left: 50%; top: 0; width: 50%; height: 100%; z-index: 1;} #footer>div#controls {bottom: 0; top: auto; height: auto;} =20 div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; margin: 0;} -#currentSlide {position: absolute; width: 10%; left: 45%; bottom: 1em; z= -index: 10;} +#currentSlide {position: absolute; width: 10%; left: 45%; bottom: 1.5em;= z-index: 10;} html>body #currentSlide {position: fixed;} =20 /* Modified: trunk/SQLObject/docs/presentation-2004-11/ui/pretty.css =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/SQLObject/docs/presentation-2004-11/ui/pretty.css 2004-11-11 18= :38:24 UTC (rev 363) +++ trunk/SQLObject/docs/presentation-2004-11/ui/pretty.css 2004-11-11 18= :57:53 UTC (rev 364) @@ -2,7 +2,8 @@ Note that the 'body' font size may have to be changed if the resoluti= on is different than expected. */ =20 -body {background: #fff url(bodybg.gif) -16px 0 no-repeat; color: #000; f= ont-size: 2em;} +/* url(bodybg.gif) */ +body {background: #fff -16px 0 no-repeat; color: #000; font-size: 2em;} :link, :visited {text-decoration: none;} #controls :active {color: #88A !important;} #controls :focus {outline: 1px dotted #227;} @@ -31,17 +32,17 @@ ul ul li {margin: .2em; font-size: 85%; list-style: square;} img.leader {display: block; margin: 0 auto;} =20 -div#header, div#footer {background: #005; color: #AAB; +div#header, div#footer {background: #005; color: #BBC; font-family: Verdana, Helvetica, sans-serif;} -div#header {background: #005 url(bodybg.gif) -16px 0 no-repeat; +div#header {background: #005 -16px 0 no-repeat; line-height: 1px;} div#footer {font-size: 0.5em; font-weight: bold; padding: 1em 0;} #footer h1, #footer h2 {display: block; padding: 0 1em;} #footer h2 {font-style: italic;} =20 div.long {font-size: 0.75em;} -.slide h1 {position: absolute; top: 0.7em; left: 87px; z-index: 1; - margin: 0; padding: 0.3em 0 0 50px; white-space: nowrap; +.slide h1 {position: absolute; top: 0.0em; left: 0px; z-index: 1; + margin: 0; padding: 0.3em 0 0 10px; white-space: nowrap; font: bold 150%/1em Helvetica, sans-serif; text-transform: capitalize; color: #DDE; background: #005;} .slide h3 {font-size: 130%;} @@ -54,13 +55,13 @@ top: auto; height: auto;} div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; margin: 0; padding: 0;} -div#controls a {font-size: 2em; padding: 0; margin: 0 0.5em;=20 +div#controls a {font-size: 1.7em; padding: 0; margin: 0 0.5em;=20 background: #005; border: none; color: #779;=20 cursor: pointer;} div#controls select {visibility: hidden; background: #DDD; color: #227;} div#controls div:hover select {visibility: visible;} =20 -#currentSlide {text-align: center; font-size: 0.5em; color: #449;} +#currentSlide {text-align: center; font-size: 0.5em; color: #77B;} =20 #slide0 {padding-top: 3.5em; font-size: 90%;} #slide0 h1 {position: static; margin: 1em 0 1.33em; padding: 0; |
From: <sub...@co...> - 2004-11-05 21:40:23
|
Author: ianb Date: 2004-11-05 12:24:01 -0500 (Fri, 05 Nov 2004) New Revision: 334 Modified: trunk/SQLObject/sqlobject/sqlbuilder.py Log: The % in LIKE expressions is quoted in different ways on different databases. This uses \% for Postgres and MySQL, %% for other databases Modified: trunk/SQLObject/sqlobject/sqlbuilder.py =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/SQLObject/sqlobject/sqlbuilder.py 2004-11-05 15:08:12 UTC (rev = 333) +++ trunk/SQLObject/sqlobject/sqlbuilder.py 2004-11-05 17:24:01 UTC (rev = 334) @@ -546,13 +546,13 @@ return SQLOp("LIKE", expr, string) =20 def STARTSWITH(expr, string): - return SQLOp("LIKE", expr, _likeQuote(string) + '%') + return SQLOp("LIKE", expr, _LikeQuoted(string) + '%') =20 def ENDSWITH(expr, string): - return SQLOp("LIKE", expr, '%' + _likeQuote(string)) + return SQLOp("LIKE", expr, '%' + _LikeQuoted(string)) =20 def CONTAINSSTRING(expr, string): - return SQLOp("LIKE", expr, '%' + _likeQuote(string) + '%') + return SQLOp("LIKE", expr, '%' + _LikeQuoted(string) + '%') =20 def ISNULL(expr): return SQLOp("IS", expr, None) @@ -560,9 +560,20 @@ def ISNOTNULL(expr): return SQLOp("IS NOT", expr, None) =20 -def _likeQuote(s): - return s.replace('%', '%%') +class _LikeQuoted: + # @@: I'm not sure what the quoting rules really are for all the + # databases =20 + def __init__(self, expr): + self.expr =3D expr + + def __sqlrepr__(self, db): + s =3D sqlrepr(self.expr, db) + if db in ('postgres', 'mysql'): + return s.replace('%', '\\%') + else: + return s.replace('%', '%%') + ######################################## ## Global initializations ######################################## |
From: <sub...@co...> - 2004-10-07 12:10:49
|
Author: bbollenbach Date: 2004-10-07 03:57:16 -0400 (Thu, 07 Oct 2004) New Revision: 273 Modified: trunk/SQLObject/sqlobject/joins.py trunk/SQLObject/tests/test.py Log: Andrew Bennetts' patch to create join methods correctly, even if they're = not named explicitly. I've done some slight renaming to hopefully more clearl= y describe what's being tested. Modified: trunk/SQLObject/sqlobject/joins.py =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/SQLObject/sqlobject/joins.py 2004-10-06 01:23:42 UTC (rev 272) +++ trunk/SQLObject/sqlobject/joins.py 2004-10-07 07:57:16 UTC (rev 273) @@ -29,6 +29,8 @@ def _get_joinMethodName(self): return self._joinMethodName =20 + joinMethodName =3D property(_get_joinMethodName, _set_joinMethodName= ) + def withClass(self, soClass): if self.kw.has_key('joinMethodName'): self._joinMethodName =3D self.kw['joinMethodName'] Modified: trunk/SQLObject/tests/test.py =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/SQLObject/tests/test.py 2004-10-06 01:23:42 UTC (rev 272) +++ trunk/SQLObject/tests/test.py 2004-10-07 07:57:16 UTC (rev 273) @@ -38,18 +38,6 @@ else: self.fail("should have raised an error on duplicate class de= finition") =20 -## class Billboard(SQLObject): -## message =3D StringCol() - -## class UnicodeTest(SQLObjectTest): -## classes =3D [Billboard] - -## def testUnicodeStrings(self): -## try: -## b =3D Billboard(message =3D u"foobar") -## except Exception, err: -## self.fail("shouldn't have raised an exception.") - ######################################## ## Basic operation ######################################## @@ -741,6 +729,12 @@ _columns =3D [StringCol('zip', length=3D5, alternateID=3DTrue)] _joins =3D [RelatedJoin('PersonJoiner')] =20 +class ImplicitJoiningSO(SQLObject): + foo =3D RelatedJoin('Bar') + +class ExplicitJoiningSO(SQLObject): + _joins =3D [MultipleJoin('Bar', joinMethodName=3D'foo')] + class JoinTest(SQLObjectTest): =20 classes =3D [PersonJoiner, AddressJoiner] @@ -772,6 +766,15 @@ def assertNamesEqual(self, people, dest): self.assertEqual([p.name for p in people], dest) =20 + def testJoinAttributeWithUnderscores(self): + # Make sure that the implicit setting of joinMethodName works + self.failUnless(hasattr(ImplicitJoiningSO, 'foo')) + self.failIf(hasattr(ImplicitJoiningSO, 'bars')) + + # And make sure explicit setting also works + self.failUnless(hasattr(ExplicitJoiningSO, 'foo')) + self.failIf(hasattr(ExplicitJoiningSO, 'bars')) + class PersonJoiner2(SQLObject): =20 _columns =3D [StringCol('name', length=3D40, alternateID=3DTrue)] |
From: <sub...@co...> - 2004-10-02 01:06:15
|
Author: bbollenbach Date: 2004-10-01 16:53:10 -0400 (Fri, 01 Oct 2004) New Revision: 260 Modified: trunk/SQLObject/sqlobject/classregistry.py trunk/SQLObject/tests/test.py Log: Steve Alexander's patch to raise an error when the same SQLObject is defined more than once. I've added a test to prove that this works. Modified: trunk/SQLObject/sqlobject/classregistry.py =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/SQLObject/sqlobject/classregistry.py 2004-10-01 17:57:08 UTC (r= ev 259) +++ trunk/SQLObject/sqlobject/classregistry.py 2004-10-01 20:53:10 UTC (r= ev 260) @@ -65,6 +65,9 @@ that other classes can find it by name. We also call any callbacks that are waiting for the class. """ + if cls.__name__ in self.classes: + raise ValueError("class %s is already in the registry" + % cls.__name__) self.classes[cls.__name__] =3D cls if self.callbacks.has_key(cls.__name__): for callback, args, kw in self.callbacks[cls.__name__]: Modified: trunk/SQLObject/tests/test.py =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/SQLObject/tests/test.py 2004-10-01 17:57:08 UTC (rev 259) +++ trunk/SQLObject/tests/test.py 2004-10-01 20:53:10 UTC (rev 260) @@ -24,6 +24,32 @@ curr_db =3D None from sqlobject import cache =20 +class ClassRegistryTest(SQLObjectTest): + def testErrorOnDuplicateClassDefinition(self): + """Raise an error if a class is defined more than once.""" + class Duplicate(SQLObject): + pass + + try: + class Duplicate(SQLObject): + pass + except ValueError, err: + self.assertEqual(str(err), "class Duplicate is already in th= e registry") + else: + self.fail("should have raised an error on duplicate class de= finition") + +## class Billboard(SQLObject): +## message =3D StringCol() + +## class UnicodeTest(SQLObjectTest): +## classes =3D [Billboard] + +## def testUnicodeStrings(self): +## try: +## b =3D Billboard(message =3D u"foobar") +## except Exception, err: +## self.fail("shouldn't have raised an exception.") + ######################################## ## Basic operation ######################################## |
From: <sub...@co...> - 2004-09-27 22:25:15
|
Author: ianb Date: 2004-09-27 14:12:38 -0400 (Mon, 27 Sep 2004) New Revision: 232 Modified: trunk/SQLObject/examples/config.py Log: Fixed bug [ 1030094 ] Documentation uses incorrect classname. (typo) Modified: trunk/SQLObject/examples/config.py =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/SQLObject/examples/config.py 2004-09-27 18:10:58 UTC (rev 231) +++ trunk/SQLObject/examples/config.py 2004-09-27 18:12:38 UTC (rev 232) @@ -11,7 +11,7 @@ conn =3D 'mysql://test@localhost/testdb' conn =3D PostgresConnection('user=3Dtest dbname=3Dtestdb') conn =3D 'postgres://test@localhost/testdb' -conn =3D SQLiteConnect('database.db') +conn =3D SQLiteConnection('database.db') conn =3D 'sqlite://path/to/database.db' conn =3D DBMConnection('database/') conn =3D 'dbm://path/to/database/' |