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)

Reply via email to