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 sqlalchemy@googlegroups.com 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)