Hi,

I encountered a strange situation when trying to delete an object from
a parent table with all its child
objects. I found that if you call objectstore.commit() after the
deletion of each child object, then you got problem when deleting the
parent object.

The parent table is called 'brand', which has a one-to-many
relationship with the child table 'series', which has a foreign key
'brand_id' referring to the 'brand' table. The object classes, tables
and mappers are defined as follows:

class Series(object):
   pass

t_series = Table('series', _engine, autoload=True)
Series.mapper = mapper(Series, t_series)

class Brand(object):
   pass

t_brands = Table('brands', _engine, autoload=True)
Brand.mapper = mapper(Brand, t_brands, properties={
   'series': relation(mapper(Series, t_series), order_by=Series.c.disp_order)
   })

Now I try to delete brand with id 1 and all series referring to it:

objectstore.clear()
b = Brand.mapper.get(1)
for s in b.series:
   objectstore.delete(s)

objectstore.delete(b)
objectstore.commit()

This works fine, SQLAlchemy groups all the deletion in one UOW and
produces the following output:

[2006-05-07 21:37:40,608] [engine]: DELETE FROM series WHERE
series.series_id = %(series_id)s
[2006-05-07 21:37:40,608] [engine]: [{'series_id': 5}, {'series_id':
4}, {'series_id': 6}, {'series_id': 1205}]
[2006-05-07 21:37:40,625] [engine]: DELETE FROM public.brands WHERE
brands.brand_id = %(brand_id)s
[2006-05-07 21:37:40,625] [engine]: [{'brand_id': 1}]

HOWEVER, if I decide to commit each deletion of the series immediately
and separately, the last deletion (of the brand object itself), will
fail:

objectstore.clear()
b = Brand.mapper.get(1)
for s in b.series:
   objectstore.delete(s)
   objectstore.commit()  ## If we commit immediately

objectstore.delete(b)
objectstore.commit()  ## The last deletion will fail

The debug output and trace looks like the following:

2006-05-07 21:36:10,280] [engine]: DELETE FROM series WHERE
series.series_id = %(series_id)s
[2006-05-07 21:36:10,280] [engine]: [{'series_id': 5}]
[2006-05-07 21:36:10,280] [engine]: DELETE FROM series WHERE
series.series_id = %(series_id)s
[2006-05-07 21:36:10,280] [engine]: [{'series_id': 4}]
[2006-05-07 21:36:10,280] [engine]: DELETE FROM series WHERE
series.series_id = %(series_id)s
[2006-05-07 21:36:10,280] [engine]: [{'series_id': 6}]
[2006-05-07 21:36:10,280] [engine]: DELETE FROM series WHERE
series.series_id = %(series_id)s
[2006-05-07 21:36:10,296] [engine]: [{'series_id': 1201}]
Traceback (most recent call last):
 File "<input>", line 1, in ?
 File "build\bdist.win32\egg\sqlalchemy\mapping\objectstore.py", line
250, in commit
 File "build\bdist.win32\egg\sqlalchemy\mapping\objectstore.py", line
81, in flush
 File "build\bdist.win32\egg\sqlalchemy\mapping\unitofwork.py", line
249, in flush
 File "build\bdist.win32\egg\sqlalchemy\mapping\unitofwork.py", line
374, in execute
 File "build\bdist.win32\egg\sqlalchemy\mapping\unitofwork.py", line
536, in execute
 File "build\bdist.win32\egg\sqlalchemy\mapping\unitofwork.py", line
459, in execute
 File "build\bdist.win32\egg\sqlalchemy\mapping\properties.py", line
483, in process_dependencies
 File "build\bdist.win32\egg\sqlalchemy\mapping\properties.py", line
579, in _synchronize
 File "build\bdist.win32\egg\sqlalchemy\mapping\sync.py", line 87, in execute
 File "build\bdist.win32\egg\sqlalchemy\mapping\sync.py", line 122, in execute
 File "build\bdist.win32\egg\sqlalchemy\mapping\mapper.py", line 497,
in _setattrbycolumn
 File "build\bdist.win32\egg\sqlalchemy\mapping\properties.py", line
33, in setattr
 File "build\bdist.win32\egg\sqlalchemy\attributes.py", line 55, in __set__
 File "build\bdist.win32\egg\sqlalchemy\attributes.py", line 355, in
set_attribute
 File "build\bdist.win32\egg\sqlalchemy\mapping\unitofwork.py", line
65, in value_changed
 File "build\bdist.win32\egg\sqlalchemy\mapping\unitofwork.py", line
182, in register_dirty
 File "build\bdist.win32\egg\sqlalchemy\mapping\unitofwork.py", line
147, in _validate_obj
InvalidRequestError: Detected a mapped object not present in the
current thread's Identity Map: '(<class '__main__.Series'>, (5,),
None)'.  Use objectstore.import_instance() to place deserialized
instances or instances from other threads

Why would the last deletion fail and why does the mapper demand a
series object from the identity map in this case? After deleting all
the 'series' objects and before the deletion of the parent 'brand'
object, I checked session.deleted, session.new, session.dirty and
session.modified_lists, and they all seem empty ('[]' shown on
console)

How can I find out what's wrong here? I am using SQLAlchemy 0.1.7 with
Python 2.4.2 and PostgreSQL 8.1.

--
Hong Yuan

大管家网上建材超市
装修装潢建材一站式购物
http://www.homemaster.cn

Reply via email to