Re: [Zope3-Users] newbie design questions for UI to external data
On Feb 13, 2006, at 10:33 PM, Shaun Cutts wrote: Um... I guess I must have asked too many questions at once :) :-) and this is still a bit much for me. I've implemented a first pass of a container for external data and got it working on some of my collections. I've included the (first pass of the) base DBContainer and DBContained objects for criticism, and would be very grateful if anyone would like to take a crack at it. It looks like an interesting approach--more low-level than I would have expected for a first cut, and *maybe* more low-level than necessary. I wish I had more time to look at it. Keep in mind, this is my first time working with the system, so I'm sure I'm going about some things the wrong way. In particular, I already think: 1) Instead of have the contained object update the container, I should rely on the event notification system. Events are a tool for disconnected notification. calling a method on an object is sending a message directly to the interested party. Not everything should be an event. If a communication between two components is a core aspect of your design, maybe direct method calls are more appropriate. 2) Right now, the system works by translating objects at the border (in IExternalContainer). Some translation is necessary, for instance, to move from mx.DateTime to datetime.datetime, but still I think I should somehow be making use of the adaptor interface. If there's not an object to adapt then you have to start somewhere. My glance at the code seemed to show that you were making a reasonable choice. Another approach might be to have an abstract row object that could represent any columns (a dict or something), and named adapters registered for the row interface plus the name of the generating table. I dunno, do what your app needs and refine it as you discover what works. 3) Along these same lines, IDBContainer._containedType should really be an interface (Object( schema = IDBContained )) Note: is there a tutorial on writing containers anywhere I should have read? I mainly figured this out by banging on it and fishing around in the code. I'd love to figure out, for instance, what is really happening with the traversals (with some interaction diagrams). I do think it was harder than it should have been. (But, then again, I think that about most things...:)) Don't know of a tutorial. Sounds like you are interested in traversal, though, which is different. Look at zope.app.traversing, or zope.app.container.traversal. The headline is that there are two kinds of traversal: URL path traversal and TALES path traversal. They have different adapters. BTW in my humble opinion, ILocation.__name__ is not well named. When I first got an error referring to __name__ I thought it was expecting a class object. And what happens when, for some strange reason, someone wants to put a class in a container, and doesn't like its default name? Not sure if that's a real use case for an ILocation. That said, it's a reasonable POV to say that Zope shouldn't claim __*__ names. But we do, so, well, yeah, we do. ;-) Gary ___ Zope3-users mailing list Zope3-users@zope.org http://mail.zope.org/mailman/listinfo/zope3-users
RE: [Zope3-Users] newbie design questions for UI to external data
Gary, Thanks for the reply. On Feb 13, 2006, at 10:33 PM, Shaun Cutts wrote: Um... I guess I must have asked too many questions at once :) :-) and this is still a bit much for me. I've implemented a first pass of a container for external data and got it working on some of my collections. I've included the (first pass of the) base DBContainer and DBContained objects for criticism, and would be very grateful if anyone would like to take a crack at it. It looks like an interesting approach--more low-level than I would have expected for a first cut, and *maybe* more low-level than necessary. I wish I had more time to look at it. If there were anything more higher-level, I would be happy I will eventually be needing paged collections to, though, so I can't give up too much control. Keep in mind, this is my first time working with the system, so I'm sure I'm going about some things the wrong way. In particular, I already think: 1) Instead of have the contained object update the container, I should rely on the event notification system. Events are a tool for disconnected notification. calling a method on an object is sending a message directly to the interested party. Not everything should be an event. If a communication between two components is a core aspect of your design, maybe direct method calls are more appropriate. The problem now is that the default edit view uses __setitem__ for the contained object, so I get one db update per column, whereas (I hope!) the event is fired after all the column updates are complete. Not a problem for the prototype, but maybe annoying in production system when Postgres is busier with other users as well. Of course, I may be overriding the default edit view anyway So the problem isn't the coupling between DBContained and DBContainer per se. Its sort of like I want the editView to enforce a transaction protocol, and want to use the Event as a proxy for commit. Note: I don't think I want dirty object in the system; otherwise I could use a lazy protocol where the container just checks -- say at demarshalling time (but probably on shutdown at least, if we are interrupted?) -- whether it has any updates to write. If I did want to go to a lazy protocol, is there a way to register things for getting triggered on shutdown. 2) Right now, the system works by translating objects at the border (in IExternalContainer). Some translation is necessary, for instance, to move from mx.DateTime to datetime.datetime, but still I think I should somehow be making use of the adaptor interface. If there's not an object to adapt then you have to start somewhere. My glance at the code seemed to show that you were making a reasonable choice. Another approach might be to have an abstract row object that could represent any columns (a dict or something), and named adapters registered for the row interface plus the name of the generating table. I dunno, do what your app needs and refine it as you discover what works. I actually do have a row object from my database code... but (as I learned) it is a bit of a pain to use directly, since: 1) datatypes are somewhat non-standard (dates, particularly), and 2) it is a bit messy to use with annotations because Row uses __getattribute__, though this is probably a design flaw in the Row class hierarchy. My thought was that I might want to be adapting my row objects (or their interfaces to be precise). Indeed, I am, but in a nonstandard way, as IExternalDatabase-derivatives do the translation. My plan now is: have the DBContained base class be a protoadapter that does all the column datatype translations and validation; its subclasses will adapt specific Row-derived classes, which have concrete sets of columns. I guess there is no particular machinery to use to present this pattern: one hierarchy adapts another hierarchy? 3) Along these same lines, IDBContainer._containedType should really be an interface (Object( schema = IDBContained )) Note: is there a tutorial on writing containers anywhere I should have read? I mainly figured this out by banging on it and fishing around in the code. I'd love to figure out, for instance, what is really happening with the traversals (with some interaction diagrams). I do think it was harder than it should have been. (But, then again, I think that about most things...:)) Don't know of a tutorial. Sounds like you are interested in traversal, though, which is different. Look at zope.app.traversing, or zope.app.container.traversal. The headline is that there are two kinds of traversal: URL path traversal and TALES path traversal. They have different adapters. When debugging, I had a lot of problems understanding what was happening because I didn't understand how traversal was trying to get data... It would just be nice to have more design doc -- especially interaction diagrams -- to refer to
RE: [Zope3-Users] newbie design questions for UI to external data
Um... I guess I must have asked too many questions at once :) I've implemented a first pass of a container for external data and got it working on some of my collections. I've included the (first pass of the) base DBContainer and DBContained objects for criticism, and would be very grateful if anyone would like to take a crack at it. Keep in mind, this is my first time working with the system, so I'm sure I'm going about some things the wrong way. In particular, I already think: 1) Instead of have the contained object update the container, I should rely on the event notification system. 2) Right now, the system works by translating objects at the border (in IExternalContainer). Some translation is necessary, for instance, to move from mx.DateTime to datetime.datetime, but still I think I should somehow be making use of the adaptor interface. 3) Along these same lines, IDBContainer._containedType should really be an interface (Object( schema = IDBContained )) Note: is there a tutorial on writing containers anywhere I should have read? I mainly figured this out by banging on it and fishing around in the code. I'd love to figure out, for instance, what is really happening with the traversals (with some interaction diagrams). I do think it was harder than it should have been. (But, then again, I think that about most things...:)) BTW in my humble opinion, ILocation.__name__ is not well named. When I first got an error referring to __name__ I thought it was expecting a class object. And what happens when, for some strange reason, someone wants to put a class in a container, and doesn't like its default name? Thanks, - Shaun - class IExternalContainer( Interface ): an external container interface. def add( obj ): add 'obj' def update( obj ): update obj def delete( obj ): delete obj def connect( obj ): establish a connection to external database def __iter__( ): iterates through external objects class IDBContainer( IContainer, ILocation ): container that lives in zope, but contains objects made persistent by an external container. _database = Object( schema = IExternalContainer ) _containedType = Attribute( type of object contained ) def __setitem__( key, obj ): Add a IDBContained object. def _updateItem( key, newKey = None ): notify that object has been modified. If the key has been changed, 'key' should be the old key of the object, and 'newKey' should be set to the new key. def __delitem__( key ): delete item, removing it both from this view and from external storage. def getAttributeNames(): get names of attributes of contained object class IDBContained( IContained, ILocation ): Contained in IDBContainer. Must notify its parent if it is changed. Because of this, it must also know how to get its key. __parent__ = Field( constraint = ContainerTypesConstraint( IDBContainer ) ) def getZopeKey(): return ascii str or unicode key. IDBContainer[ '__setitem__' ].precondition = ItemTypePrecondition( IDBContained ) class DBContainer( IterableUserDict, Contained ): Implements L{IDBContainer} from zope.interface.verify import verifyClass verifyClass( IDBContainer, DBContainer ) True implements( IDBContainer ) # prevent browser from choosing name nameAllowed = False # defaults for IDBContainer _database = None _containedType = None # defaults for ILocation (base of IDBContainer) __parent__ = None __name__ = None def __init__( self ): super( DBContainer, self ).__init__( ) self.__setstate__() def _filterKey( self, newKey ): check that key is ascii string or unicode, and convert to unicode. if newKey is None: TypeError( key can't be None ) elif isinstance( newKey, str ): try: newKey = unicode( newKey ) except UnicodeError: raise TypeError(name not unicode or ascii string) elif not isinstance( newKey, unicode ): raise TypeError( key '%s' must be ascii or unicode string % repr( newKey ) ) return newKey def __setitem__( self, key, obj ): Implements L{cranedata.web.interfaces.IFundContainer.__setitem__}. key = self._filterKey( key ) if not isinstance( obj, self._containedType ): raise TypeError( object '%s' must be a %s % ( repr( obj ), self._containedType.__name__ ) ) self._database.add( obj ) setitem( self, self.data.__setitem__, key, obj ) def __getitem__( self, key ): return self.data[ self._filterKey( key ) ] def __delitem__( self, key ): obj = self.data.pop(
[Zope3-Users] newbie design questions for UI to external data
Hello, I'm trying to write a zope3 UI for data in an RDBMS with python business logic. (Hence updates shouldn't go directly to the database; queries are probably best off using the marshalling code in business logic as well, but could use direct query if its especially easy.) I was trying to adapt Stephan Ritcher's Zope3 book examples, but am getting a bit snarled with escaping from/interacting with the ZODB. For a collection of objects that live in the DDBMS, should I try to implement a collection object that itself lives in the ZODB (inherits from Persistent, implements IContainer), but really performs a query to get its data? Then I need to mark actual contents volatile (?) Is there a hook to set the volatile contents? Also, it would be good to have a simple admin object in the ZODB that has DB connection info. Putting one there and making it accessible via Zope3 seems easy enough -- just inherit from Persistent But how do I access this info from, say, my collection object(s). Does Persistent generate a standard key? Or should I have one big database object in ZODB that has attributes for the connection, and also attributes for each of the collections. This seems easy enough, but then I wonder if the zcml config will become tricky, as I would have to declare forms for attributes of instances (somehow)? When using the standard forms, I got errors for not setting __name__ and __parent__ on my contained objects. The first was easy enough, but who is supposed to set __parent__? I want to use standard forms if possible for the moment, as I am trying to get up a prototype fast. Eventually I will need a paged view. Is there one out there to use? In general, if there is a good example of something like I am trying to do, I would be grateful if someone could point me to it. - Shaun Cutts ___ Zope3-users mailing list Zope3-users@zope.org http://mail.zope.org/mailman/listinfo/zope3-users