[sqlalchemy] Re: preview an update?

2009-06-23 Thread Catherine Devlin
On Thu, Jun 18, 2009 at 4:57 PM, Michael Bayermike...@zzzcomputing.com wrote:

 you can get most of this stuff from the session without any flush
 occurring.  at the object level are the new, dirty, and deleted
 collections on Session.   At the attribute level the
 attributes.get_history() method will illustrate the full changes made
 since the last flush on an individual object attribute.  get_history()
 should be in the API docs.

Thank you!  get_history is just what I needed.  However, I spent a
long time being confused by something that I think is a very obscure
bug.

If you make an update to a table that has a relation to a view, then
get_history on an attribute in the view, then the history is
forgotten.  I'm attaching a file that duplicates it completely, but
here's the gist of it (after setting up as described in the docs' ORM
tutorial):

 ed = session.query(User).first()
 print sqlalchemy.orm.attributes.get_history(ed, 'fullname')
((), [u'Ed Jones'], ())
 ed.fullname = 'Eduardo Jones'
 print sqlalchemy.orm.attributes.get_history(ed, 'fullname')
(['Eduardo Jones'], (), [u'Ed Jones'])
 # so far so good
 print sqlalchemy.orm.attributes.get_history(ed.userview, 'name')
((), [u'ed'], ())
 print sqlalchemy.orm.attributes.get_history(ed, 'fullname')
((), ['Eduardo Jones'], ())

... now get_history thinks fullname has always been Eduardo Jones.

Is this a bug I should file, or something I should have expected?

Thanks as always!
-- 
- Catherine
http://catherinedevlin.blogspot.com/
*** PyOhio * July 25-26, 2009 * pyohio.org ***

--~--~-~--~~~---~--~~
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 
sqlalchemy+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---

from sqlalchemy import create_engine
engine = create_engine('sqlite:///:memory:', echo=False)
from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey
metadata = MetaData()
users_table = Table('users', metadata,
Column('id', Integer, primary_key=True),
Column('name', String(12)),
Column('fullname', String(40)),
Column('password', String(40))
)
metadata.create_all(engine) 

class User(object):
def __init__(self, name, fullname, password):
self.name = name
self.fullname = fullname
self.password = password

def __repr__(self):
   return User('%s','%s', '%s') % (self.name, self.fullname, self.password)

from sqlalchemy.orm import mapper, relation, backref
mapper(User, users_table) 

ed_user = User('ed', 'Ed Jones', 'edspassword')

from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind=engine)
session = Session()
session.add(ed_user)
session.commit()

#
# everything up to here comes from the ORM tutorial at http://www.sqlalchemy.org/docs/05/ormtutorial.html

engine.connect().execute('CREATE VIEW userview AS SELECT id, name FROM users')
class UserView(object):
pass
userview = Table('userview', metadata, 
 Column('id', ForeignKey('users.id'), primary_key=True), 
 autoload=True, autoload_with=engine)
mapper(UserView, userview,
   properties={'user': relation(User, uselist=False, lazy=True,
   backref=backref('userview', uselist=False))})   

import sqlalchemy.orm.attributes
ed = session.query(User).first()
print sqlalchemy.orm.attributes.get_history(ed, 'fullname')
ed.fullname = 'Eduardo Jones'
print sqlalchemy.orm.attributes.get_history(ed, 'fullname')
print sqlalchemy.orm.attributes.get_history(ed.userview, 'name')
print sqlalchemy.orm.attributes.get_history(ed, 'fullname')


[sqlalchemy] Re: preview an update?

2009-06-23 Thread Michael Bayer

Catherine Devlin wrote:
 (['Eduardo Jones'], (), [u'Ed Jones'])
 # so far so good
 print sqlalchemy.orm.attributes.get_history(ed.userview, 'name')
 ((), [u'ed'], ())
 print sqlalchemy.orm.attributes.get_history(ed, 'fullname')
 ((), ['Eduardo Jones'], ())

 ... now get_history thinks fullname has always been Eduardo Jones.

 Is this a bug I should file, or something I should have expected?

I'm going to say that ed.userview did a lazy load and triggered an
autoflush.  the autoflush will reset the attributes back to unchanged so
that is expected.

--~--~-~--~~~---~--~~
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 
sqlalchemy+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: preview an update?

2009-06-18 Thread Michael Bayer


On Jun 18, 2009, at 4:27 PM, Catherine Devlin wrote:


 I'm building a complex ETL tool with SQLAlchemy that will sometimes
 need to let the user preview a changed record - not actually carry
 out the update, just find out which fields would be changed, and to
 what values.  I'm having trouble figuring out a good way to do it.

 Is there a way to get a sort of preview of an update - get information
 about what updates are pending for the next flush, or get a flush to
 report back its changes without actually performing it?

you can get most of this stuff from the session without any flush  
occurring.  at the object level are the new, dirty, and deleted  
collections on Session.   At the attribute level the  
attributes.get_history() method will illustrate the full changes made  
since the last flush on an individual object attribute.  get_history()  
should be in the API docs.

what you can't get without flushing are newly generated primary key  
identifiers, server side defaults that havent fired off, and the  
results of cascades like delete or on update cascades.  however if  
this does what you need this is definitely the best way to go as it  
doesnt require issuing a flush() which then has to be rolled back  
internally.


 One approach would be to set echo to True, let the user see the echo,
 then roll back the transaction, but there are a couple reasons I don't
 think that will work for me.  I don't want to make the user mentally
 parse SQL statements; I'd like to get a dict of the pending changes
 and write my own code to display them attractively, instead.  Anyway,
 I'm having trouble getting echo to work on this app.  (The whole thing
 is part of a TurboGears project, and my echoes aren't showing up,
 probably because of something I don't understand about the SQLAlchemy
 logging configuration.)

If you went down this road, use a ConnectionProxy to capture all the  
SQL expressions as they are emitted.this is also in the API  
docs.The unit tests use a custom proxy to capturing SQL and  
expressions  various operations, using a decorator to enable/disable  
the capturing.   you can capture the SQL expression constructs  
directly where you'll have access to all the Table objects and bind  
params and you can display it in any way suitable.

Depending on your needs I'd use one of the above approaches and I  
would not recommend instrumenting any of the ORM internals.



--~--~-~--~~~---~--~~
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 
sqlalchemy+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---