Hi,
I think I have detected two bugs for PostgreSQL databases. I don't think is
a different behavior between 0.6 and 0.7 as there is no problem with sqlite.
In the following code I have a very simple model, which contains a helper
"pre-process" list class, which just transforms strings into the correct
model object. Very similar to @collection.converter, however it works for
append and extend. It works without errors (obviously w/o MutableComposite)
in 0.6 and sqlite in 0.7. But the strange part is that it follows different
behavior depending on when you add the parent object to the session: if you
add it after creating the "company" (just like I put it here), then it will
not insert the "data" object (see the produced log, if you change it to the
sqlite engine it will work); but if you add and commit it, before and after
the append operation it will fail with an IntegrityError in both sqlite and
postgresql (this is why I doubt if it is a different behavior or not).
Either way I think that adding the object, committing it, then adding it
again, and committing it, although very wasteful, should behave without
error, specially for occasions when you don't know how your object has been
handled in relation with your database.
The other bug is just for PostgreSQL and can be reproduced by uncommenting
the "metadata.create_all()" line of code. It appears that is trying to
create the data table before company. It doesn't matter in which order are
defined the same error is raised.
Thanks
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, \
String, ForeignKey
from sqlalchemy.orm.collections import collection
from sqlalchemy.orm import mapper, relationship, sessionmaker, composite
from sqlalchemy.ext.mutable import MutableComposite
engine = create_engine('postgresql://user:password@localhost:5432/db',
echo=True)
#engine = create_engine('sqlite:///:memory:', echo=True)
metadata = MetaData()
company = Table(
'company',
metadata,
Column('id', Integer, primary_key=True),
Column('name', String, nullable=False),
Column('x', Integer, nullable=False),
Column('y', Integer, nullable=False),
sqlite_autoincrement=True
)
data = Table(
'data',
metadata,
Column('id', Integer, primary_key=True),
Column('data', String, nullable=False),
Column('company', Integer, ForeignKey('company.id'), nullable=False),
sqlite_autoincrement=True
)
metadata.bind = engine
#metadata.drop_all()
company.create()
data.create()
#metadata.create_all()
class PreprocessList(list):
def __init__(self, items=None):
if items is None:
super(PreprocessList, self).__init__()
else:
super(PreprocessList, self).__init__(
[self.preprocess(item) for item in items]
)
def preprocess(self, value):
return value
def __setitem__(self, key, value):
value = self.preprocess(value)
super(PreprocessList, self).__setitem__(key, value)
@collection.internally_instrumented
def append(self, value):
value = self.preprocess(value)
super(PreprocessList, self).append(value)
@collection.internally_instrumented
def extend(self, items):
for item in items:
self.append(item)
class AppenderList(PreprocessList):
def preprocess(self, value):
v = Data()
v.data = value + 'data'
return v
def __getitem__(self, key):
value = super(AppenderList, self).__getitem__(key)
return value.data
class Data(object):
pass
class Company(object):
pass
class Point(MutableComposite):
def __init__(self, x, y):
self.x = x
self.y = y
def __setattr__(self, key, value):
"Intercept set events"
# set the attribute
object.__setattr__(self, key, value)
# alert all parents to the change
self.changed()
def __composite_values__(self):
return self.x, self.y
def __eq__(self, other):
return isinstance(other, Point) and \
other.x == self.x and \
other.y == self.y
def __ne__(self, other):
return not self.__eq__(other)
mapper(Company, company, properties={
'id': company.c.id,
'name': company.c.name,
'data': relationship(
Data,
collection_class=AppenderList
),
'point': composite(Point, company.c.x, company.c.y)
})
mapper(Data, data)
session = sessionmaker(bind=engine)()
# CHANGE BELOW
c = Company()
c.name = 'company 1'
c.data = AppenderList()
c.point = Point(3, 5)
session.add(c)
session.commit()
c.data.append('a')
#session.add(c)
session.commit()
metadata.drop_all()
print 'ok'
--
You received this message because you are subscribed to the Google Groups
"sqlalchemy" group.
To view this discussion on the web visit
https://groups.google.com/d/msg/sqlalchemy/-/9vkKkIpe9aEJ.
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.