Dear Michael,

thanks for the detailed response.

On Mittwoch, 21. August 2013 16:55:18 Michael Bayer wrote:
> On Aug 21, 2013, at 12:40 PM, Hans-Peter Jansen <h...@urpla.net> wrote:
> > Hi,
> > 
> > being new to SQLAlchemy, I try to get my way through it.
> > 
> > In an application, I have rather elaborate needs to track changes.
> > 
> > I've defined 3 classes with declarative, where the main class has
> > relationships with two auxiliary classes, that refer to the main class
> > with foreign references. All pretty basic stuff.
> > 
> > Now I need to track all modifications to all fields, including the
> > relationship list objects.
> > 
> > What is the best way to accomplish this task with SQLAlchemy?
> > 
> > Is there some boilerplate available to support this, or do I have to carry
> > around two objects and compare them item by item?
> you can intercept changes on attributes using the attribute event system: 
> http://docs.sqlalchemy.org/en/rel_0_8/orm/events.html#attribute-events
> 
> otherwise you can get at the changes on an attribute after the fact (but
> before a flush) using the history interface:
> http://docs.sqlalchemy.org/en/rel_0_8/orm/session.html?highlight=history#sq
> lalchemy.orm.attributes.History

Hmm, it looks like that needs to be applied on every column attribute..

> you can get a list of all attributes mapped using mapper.attrs:
> http://docs.sqlalchemy.org/en/rel_0_8/orm/mapper_config.html?highlight=mapp
> er.attrs#sqlalchemy.orm.mapper.Mapper.attrs

from here. Since I need to track all 'net' changes, that's rather unpractical.

I've changed my code to cope with best practices hopefully (from what I can 
extract from the overwhelming docs).

That is: just change instance attributes in place, and check for dirtiness 
later on, with session.is_modified(obj, passive = True). I've chosen this 
interface, because the fact of *really* being modified is essential here.

This scheme would do just fine, but doesn't work as expected.

See attached code. Depending on modification state of parent (line 51), the 
modification state of the child is detected (parent changed) or not (parent 
unchanged).

In my real code, it's the other way around, modifications to relations are 
detected (as I iterate through all of them), but changes to the "parent" 
aren't, although the correct SQL UPDATE code is executed after commit(). Since 
it isn't detected correctly, my app falls flat on its nose..

Do you have any idea on this one?

Cheers,
Pete

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/groups/opt_out.
from sqlalchemy import Column, Integer, String, ForeignKey, create_engine
from sqlalchemy.orm import relationship, backref, sessionmaker
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

ERR = 1

class Parent(Base):
    __tablename__ = 'parent'
    id = Column(Integer, primary_key=True)
    name = Column(String(100), nullable = False, index = True)
    children = relationship('Child', backref = 'parent',
                            single_parent = True, # lazy = 'joined',
                            cascade = 'all, delete-orphan')


    def __repr__(self):
        cl = [repr(c) for c in self.children]
        return 'Parent(%r, children: %s)' % (self.name, ', '.join(cl))

class Child(Base):
    __tablename__ = 'child'
    id = Column(Integer, primary_key=True)
    name = Column(String(100), nullable = False, index = True)
    parent_id = Column(Integer, ForeignKey('parent.id'), nullable = False)

    def __repr__(self):
        return 'Child(%r)' % (self.name)

if __name__ == '__main__':
    engine = create_engine('sqlite://', echo = True)
    Base.metadata.create_all(engine)
    session = sessionmaker(engine, expire_on_commit=False)()

    def pr(obj, exp, msg):
        res = session.is_modified(obj, passive = True)
        print msg + ',', 'expected: %s,' % exp, 'outcome: %s,' % res, exp == res and 'okay' or 'FAILED'

    p = Parent(name = 'pa')
    c1 = Child(name = 'li')
    p.children.append(c1)

    print 'Starting with:', p

    session.add(p)
    session.commit()

    pr(p, False, 'initial session committed')

    if ERR:
        pr(p, False, 'parent not renamed')
    else:
        p.name = 'po'
        pr(p, True, 'parent renamed to "po"')

    c1.name = 'lo'
    pr(c1, True, 'child renamed to "lo", testing child')
    pr(p, True, 'child renamed to "lo", testing parent')

    session.commit()


Reply via email to