Here is a test case for the bug. The bug only manifests itself if the
transaction that slices the list also modifies all the remaining objects in the
list.
Barry
----- Original Message ----
From: Michael Bayer <[EMAIL PROTECTED]>
To: [email protected]
Sent: Wednesday, October 24, 2007 5:01:43 PM
Subject: [sqlalchemy] Re: Problem when slicing a relation list
hey Barry -
again, can you please attach a working test case for this one ? attached is
mine, which tests this exact operation for four diferent kinds of relation()s -
one-to-many and many to many, with and without delete cascade on the relation.
passes for 0.3 (including 0.3.10) and 0.4.
-----Inline Attachment Follows-----
from sqlalchemy import *
from sqlalchemy.orm import *
def test(m2m=False, cascade=False, useclear=False):
engine = create_engine('sqlite://', echo=True)
meta = MetaData(engine)
a = Table('a', meta, Column('id', Integer, primary_key=True), Column('foo',
String(30)))
if m2m:
b = Table('b', meta, Column('id', Integer, primary_key=True),
Column('foo', String(30)))
else:
b = Table('b', meta, Column('id', Integer, primary_key=True),
Column('foo', String(30)), Column('a_id', Integer, ForeignKey('a.id')))
if m2m:
atob = Table('atob', meta, Column('a_id', Integer, ForeignKey('a.id')),
Column('b_id', Integer, ForeignKey('b.id')), )
else:
atob = None
class A(object):
def __init__(self, foo):
self.foo = foo
class B(object):
def __init__(self, foo):
self.foo = foo
if cascade:
use_cascade = "all, delete-orphan"
else:
use_cascade = "save-update"
mapper(A, a, properties={
'bs':relation(B, secondary=atob, cascade=use_cascade)
})
mapper(B, b)
meta.create_all()
a1 = A('a1')
a1.bs.append(B('b1'))
a1.bs.append(B('b2'))
a1.bs.append(B('b3'))
sess = create_session()
sess.save(a1)
sess.flush()
if m2m:
assert atob.count().scalar() == 3
else:
assert b.count(b.c.a_id == None).scalar() == 0
assert b.count().scalar() == 3
if useclear:
sess.clear()
a1 = sess.query(A).get(a1.id)
assert len(a1.bs) == 3
a1.bs = a1.bs[1:]
sess.flush()
if m2m:
assert atob.count().scalar() == 2
else:
assert b.count(b.c.a_id != None).scalar() == 2
if cascade:
assert b.count().scalar() == 2
else:
assert b.count().scalar() == 3
if useclear:
sess.clear()
a1 = sess.query(A).get(a1.id)
assert len(a1.bs) == 2
for m2m in (True, False):
for cascade in (True, False):
for useclear in (True, False):
test(m2m, cascade, useclear)
On Oct 24, 2007, at 4:18 PM, Barry Hart wrote:
I found a problem in SqlAlchemy 0.3.10 this week: slicing a list property
caused the whole association list to be deleted. For example, suppose I want to
remove the first 3 items from a list of related items:
my_obj.related = my_obj.related[3:]
When these changes were saved , my_obj.related was empty the next time it
loaded. This code, however, worked correctly:
for i in range(0, 3):
del my_obj.related[0]
Barry
__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around
http://mail.yahoo.com
__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around
http://mail.yahoo.com
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"sqlalchemy" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/sqlalchemy?hl=en
-~----------~----~----~----~------~----~------~--~---
from sqlalchemy import *
from sqlalchemy.ext.assignmapper import assign_mapper
import sqlalchemy
from sqlalchemy.ext import activemapper, sessioncontext
import datetime
engine = None
def create_engine():
global engine
engine = sqlalchemy.create_engine('sqlite://')
#engine = sqlalchemy.create_engine('postgres://postgres:[EMAIL
PROTECTED]:5432/testdb')
metadata.connect(engine)
def create_session():
return sqlalchemy.create_session(bind_to=engine)
def fuzzy_search(column, value):
"""Case insensitive search allowing partial string matches."""
return func.lower(column).like('%%%s%%' % value.lower())
metadata = activemapper.metadata
create_engine()
session = activemapper.Objectstore(create_session)
activemapper.objectstore = session
g_count = 10
##########################################################################
# Classes
##########################################################################
class ObjX(object):
def __init__(self):
self.y = ObjY()
def analyze(self):
#del self.y.zs[0] # Using this line works.
self.y.zs = self.y.zs[1:] # Using this line fails.
for i in range(0, len(self.y.zs)):
s = self.y.zs[i]
# Modify the other objects in the list. Without this, the test
still works.
s.units = 10
#
class ObjY(object): pass
class ObjZ(object): pass
##########################################################################
# Tables
##########################################################################
obj_x_table = Table(
'obj_x', metadata,
Column('id', Integer, primary_key=True),
Column('desc', Unicode(255), default="")
)
obj_y_table = Table(
'obj_y', metadata,
Column('id', Integer, primary_key=True),
Column('obj_x_id', Integer, ForeignKey('obj_x.id'))
)
obj_z_table = Table(
'predict_time_period', metadata,
Column('id', Integer, primary_key=True),
Column('obj_y_id', Integer, ForeignKey('obj_y.id')),
Column('start_date', DateTime),
Column('units', Float)
)
##########################################################################
# Mappings
##########################################################################
assign_mapper(session.context, ObjX, obj_x_table, properties=dict(
y=relation(ObjY, uselist=False)
))
assign_mapper(session.context, ObjY, obj_y_table, properties=dict(
zs=relation(ObjZ, order_by=obj_z_table.c.start_date,
cascade="all,delete-orphan")
))
assign_mapper(session.context, ObjZ, obj_z_table)
##########################################################################
# Functions
##########################################################################
def createX():
x = ObjX()
for i in range(0, g_count):
z = ObjZ()
z.units = i
x.y.zs.append(z)
return x
##########################################################################
# Main program
##########################################################################
obj_x_table.create(checkfirst=True)
obj_y_table.create(checkfirst=True)
obj_z_table.create(checkfirst=True)
x = createX()
assert len(x.y.zs) == g_count
session.flush()
session.clear()
x = ObjX.select()[0]
assert len(x.y.zs) == g_count # Starts with g_count objects
x.analyze() # Slices one object from the list
assert len(x.y.zs) == g_count - 1 # Check the count again
session.flush()
session.clear()
x = ObjX.select()[0]
assert len(x.y.zs) == g_count - 1, 'Expected %d, got %d' % (g_count - 1,
len(x.y.zs))