Hi,
I spent a few hours investigating a strange but very simple problem:
by any chance I'm missing something obvious...
Elaborating on Paul's "proc_hash", I now have a similar behaviour on
all my entities. It's not a very important feature, but it surely
makes it easier to introduce test data into the database.
It's a little bit different from Paul's goal, in that I wanna be able
to say, for example:
newobj = MyObject()
session.save(newobj)
newobj.updateFromDictionary(dict(
attribute1='simple attribute',
related_obj=dict(id=1, description='related obj'),
other_objs=[dict(id=1, index=1, ref=dict(description='new ref obj')),
dict(id=2, index=2, ref=dict(id=5))]
))
session.commit()
that is, being able to specify either "new subobjects" or "existing
one": in the former case, using a dictionary containing (maybe) the PK
and other field value; in the latter, with a dictionary carrying just
the primary key of the record. In the example above, "related_obj" will
be assigned a new instance of the class involved by the relation,
while "other_objs" will contain two entities, both newly created, but
one is attached to a new instance while the other should reference an
existing record fetched by primary key (that ``ref=dict(id=5)``).
The function is working, but in that particular case it fails: in the
example above, whenever it reaches the second "other_objs" where the
"ref" is loaded with something like
ref = session.query(RefObj).get(5)
I can see that SA flushes all pending operation *before* issueing the
SELECT to load the RefObj: in particular it flushes the whole "newobj"
but at that point it contains an incomplete object and I get a
constraint violation on "refid is NULL": that's obvious, since it
isn't assigned yet... but I miss the point! Why the query.get()
triggers the flush? Accordingly to the docs, it should not...
I tried using autoflush=False on the session, as well as with explicit
transactions, with no luck.
I'm attaching the function, should a kind soul shed some light on it:
the method on my instances just call that function.
But I bet the problem is somewhere else, since I get the same trouble
if I manually do that in a single session like
refobj1 = RefObj(id=1, description="one")
session.save(refobj1)
refobj2 = RefObj(id=2, description="two")
session.save(refobj2)
...
cross = CrossObj(id=1, index=1)
cross.ref = session.query(RefObj).get(1)
newobj.other_objs.append(cross)
What am I missing?
Thanks in advance,
ciao, lele.
--
nickname: Lele Gaifax | Quando vivrò di quello che ho pensato ieri
real: Emanuele Gaifas | comincerò ad aver paura di chi mi copia.
[EMAIL PROTECTED] | -- Fortunato Depero, 1929.
--~--~---------~--~----~------------~-------~--~----~
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
-~----------~----~----~----~------~----~------~--~---
def updateFromDictionary(obj, data, mapper=None):
"""Update a mapped class with data from a Python nested hash/list structure.
The `data` dictionary may contain either single scalar attributes, like
``progressivo = 1``, single dictionary representing a related entity,
or lists of dictionaries for the one-to-many relations.
A single entity is represented by a single dictionary: if it contains just
the primary key, the entity is looked up in the database, otherwise a new
entity is forged and updated with the items in the dictionary.
Example::
iterp.updateFromDictionary(dict(
# A scalar value
descrizione=u"Esempio di iter procedurale",
# A foreign key entity, looked up by primary key
contesto = dict(idcontesto="ANAG"),
# A list of entities for a one-to-many relation: each entity
# in the list has a sub-entity, the first is created new as
# there is no primary key, while the second gets loaded from
# the database, looked up by its primary key.
fasiiterprocedurale = [dict(progressivo=1, fase=dict(descrizione=u"Fase estemporanea")),
dict(progressivo=2, fase=dict(idfase=1))],
))
"""
from sqlalchemy.exceptions import ArgumentError
from sqlalchemy.orm import object_session, object_mapper
from sqlalchemy.orm.properties import PropertyLoader
if not mapper:
mapper = object_mapper(obj)
session = object_session(obj)
for col in mapper.mapped_table.c:
if not col.primary_key and data.has_key(col.name):
setattr(obj, col.name, data[col.name])
xx = [(a,b) for a,b in getattr(mapper, '_Mapper__props').items()
if isinstance(b, PropertyLoader)]
for rname,rel in xx:
if data.has_key(rname) and data[rname] is not None:
pkey = [c for c in rel.mapper.mapped_table.columns if c.primary_key]
value = data[rname]
if isinstance(value, dict):
pkvalue = [value[c.name] for c in pkey if c.name in value]
# if its just the primary key, load it
if len(pkey)==len(value)==len(pkvalue):
subobj = session.query(rel.mapper.class_).get(*pkvalue)
setattr(obj, rname, subobj)
else:
subobj = rel.mapper.class_()
setattr(obj, rname, subobj)
updateFromDictionary(subobj, value, rel.mapper)
else:
dbdata = getattr(obj, rname)
for row in value:
pkvalue = [row[c.name] for c in pkey if c.name in row]
# if its just the primary key, load it
if len(pkey)==len(row)==len(pkvalue):
subobj = session.query(rel.mapper.class_).get(pkvalue)
dbdata.append(subobj)
else:
subobj = rel.mapper.class_()
dbdata.append(subobj)
updateFromDictionary(subobj, row, rel.mapper)