Hi all,
I am doing some tests with InheritableSQLobject and have run into a problem
which I hope to get some feedback on.
The problem is that when creating a new object based on an
InheritableSQLObject class, and the creation of the child object fails after
a new parent was created, the parent record will remain in the database
without the relevant child.
Here is a short example:
~~~~~~~~~~ file: inheritTest3.py ~~~~~~~~~~~~
from sqlobject import *
from sqlobject.inheritance import InheritableSQLObject
__connection__ = "sqlite:/:memory:"
class Vehicle(InheritableSQLObject):
vid = StringCol(notNone=True, default=None, unique=True)
wheels = IntCol()
class Bicycle(Vehicle):
seats = IntCol(notNone=True, default=None)
class Car(Vehicle):
doors = IntCol(notNone=True, default=None)
def createTables():
Vehicle.createTable()
Bicycle.createTable()
Car.createTable()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Here is a sample session showing the problem when trying to create a bicycle
but only supplying the args required for the Vehicle part of the object:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In [1]: from inheritTest3 import *
In [2]: createTables(True)
In [3]: b = Bicycle(vid="id1", wheels=4)
ERROR: An unexpected error occurred while tokenizing input
The following traceback may be corrupted or invalid
The error message is: ('EOF in multi-line statement', (22, 0))
---------------------------------------------------------------------------
IntegrityError Traceback (most recent call last)
/home/tomc/mobilereporter.repo/main/app/<ipython console> in <module>()
/usr/lib/python2.5/site-packages/sqlobject/main.pyc in __init__(self, **kw)
1221 id = None
1222
-> 1223 self._create(id, **kw)
1224
.... snipping lots of traceback parts ............
/usr/lib/python2.5/site-packages/sqlobject/sqlite/sqliteconnection.pyc in
_executeRetry(self, conn, cursor, query)
187 raise DuplicateEntryError(msg)
188 else:
--> 189 raise IntegrityError(msg)
190 except self.module.InternalError, e:
191 raise InternalError(ErrorMessage(e))
IntegrityError: bicycle.seats may not be NULL
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you now supply all required args for the bicycle, using the same field
values for the vid and wheels as before, a duplicate entry error is raised:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In [4]: b = Bicycle(vid="id1", wheels=4, seats=2)
ERROR: An unexpected error occurred while tokenizing input
The following traceback may be corrupted or invalid
The error message is: ('EOF in multi-line statement', (22, 0))
---------------------------------------------------------------------------
DuplicateEntryError Traceback (most recent call last)
/home/tomc/mobilereporter.repo/main/app/<ipython console> in <module>()
/usr/lib/python2.5/site-packages/sqlobject/main.pyc in __init__(self, **kw)
1221 id = None
1222
-> 1223 self._create(id, **kw)
1224
1225 for func in post_funcs:
.... snipping lots of traceback parts ............
/usr/lib/python2.5/site-packages/sqlobject/sqlite/sqliteconnection.pyc in
_executeRetry(self, conn, cursor, query)
185 msg = ErrorMessage(e)
186 if msg.startswith('column') and msg.endswith('not
unique'):
--> 187 raise DuplicateEntryError(msg)
188 else:
189 raise IntegrityError(msg)
DuplicateEntryError: column vid is not unique
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The duplicate entry is because of the parent vehicle record was created when
the first bicycle creation failed:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In [5]: q = "SELECT * from vehicle"
In [6]: Vehicle._connection.queryAll(q)
Out[6]: [(1, 'id1', 4, 'Bicycle')]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
So, the problem is that if a new child object is created, but fails after the
parent has been created, the parent is left in tact in the database.
I'm attaching a patch as a possible fix for this, but I am not sure under
which other circumstances this patch will cause other problems. Maybe a flag
is needed inside the block that creates the child (parentCreatedHere=True),
and only if this flag is set, should the parent be deleted in exception if
the child creation failed?
I would appreciate if someone with more insight into this can look at the
patch an modify where neccessary.
BTW: This patch is against SQLObject 0.11.0.
Cheers,
Tom
--- inheritance/__init__.py.orig 2009-12-07 14:47:31.000000000 +0000
+++ inheritance/__init__.py 2009-12-07 16:35:39.000000000 +0000
@@ -384,7 +384,19 @@
id = self._parent.id
- super(InheritableSQLObject, self)._create(id, **kw)
+ # TC: Create this record and catch all exceptions in order to destroy
+ # TC: the parent if the child can not be created.
+ try:
+ super(InheritableSQLObject, self)._create(id, **kw)
+ except:
+ # TC: If we are the child, destroy the parent
+ if self.sqlmeta.parentClass:
+ self._parent.destroySelf()
+ #TC: Do we need to do this??
+ self._parent = None
+ # TC: Reraise the original exception
+ raise
+
def _findAlternateID(cls, name, dbName, value, connection=None):
result = list(cls.selectBy(connection, **{name: value}))
------------------------------------------------------------------------------
Join us December 9, 2009 for the Red Hat Virtual Experience,
a free event focused on virtualization and cloud computing.
Attend in-depth sessions from your desk. Your couch. Anywhere.
http://p.sf.net/sfu/redhat-sfdev2dev
_______________________________________________
sqlobject-discuss mailing list
sqlobject-discuss@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/sqlobject-discuss