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