Well.. I've started down the long winding road of porting EMarket to ZPatterns. I'm sure I'll learn a lot. ;-) Every time I start something I discover that my understanding is shakey.. but getting less so as time goes on.. Here is a concrete problem. (I always work better with concrete problems... even though I occasionally aspire to an abstract thought or two... that usually comes later...) I've got a class: class Shopper( OFS.SimpleItem.Item, ZPatterns.DataSkins.DataSkin, Acquisition.Implicit, AccessControl.Role.RoleManager, ): As you can see this class now inherits from data skin. I've got a specialist that manages the shopper objects. My previous constructor looked like this: def __init__(self, id, email, name, passwd, address='', city='', state='', phone='', fax='', country='', zip='', company='' ): """ Create a new shopper instance. """ # self.id=id self.email = email self.name = name self.passwd = passwd self.address = address self.city = city self.state = state self.phone = phone self.fax = fax self.zip = zip self.company = company self.country = country self.transNum = 1 self.transDict = {} self.basket = Basket(self) # firm offers... self.pendingBasket = Basket(self) # not yet firm offers.... self.pendingBasket.ClearTransaction() self.salesItems = SalesBooth(self) self.transID = self.createTransID() self.addTime = time.time() and my addShopper method used to look (essentially) like this: def addShopper(self, id, email, name, passwd, passwd2, address, city, state, phone, fax, country, zip, REQUEST=None, company = '', passExcept = 0, noCheck = 0): """Add an shopper to a folder The argument 'self' will be bound to a folder. The arguments, 'id', 'passwd', and 'passwd2' just get us started... """ # # a bunch of code to validata passwords etc.... and then... # newShopper = Shopper(id, email, name, passwd, address, city, state, phone, fax, country, zip, company) self._setObject(id, newShopper ) # get a 'result' blah blah... return result Moving this to ZPatterns brings up all sorts of questions. First of all I no longer know that the actual class stored my shopper specialist is a 'Shopper'. It could be any class kept in the shopper manager's rack(s). Soo... my addShopper should probably become: def addShopper(self, id, REQUEST): """ add a new shopper... self is now the shopperManager (Specialist) """ newShopper = self.newItem( id ) newShopper.propertysheets.methodToUpdatePropertySheetsFromMappingObject( REQUEST ) Looking through VirtualSheets and PropertySheets code I see __propsets__ so I was thinking something like: for prop in newShopper.propertysheets.__propsets__(): setattr(newShopper, prop, REQUEST.get(prop,None)) but this doesn't seem to work.... (I get 'nameError 'client'') in the debugger.... I have no clue where 'client' comes from, reminds me of the 'DTML Method' argument called 'client'... but I don't see a traceback in the debugger.. anyway.. I've come to something like this, though it doesn't do what I need.... : newShopper = self.newItem(id) newShopper.passwd = passwd newShopper.propertysheets.manage_addPropertySheet(id='EMarketProps', ns='') emp = newShopper.propertysheets.get('EMarketProps') emp.manage_addProperty('email', '', 'string') emp.manage_addProperty('name','','string') # # ... blah blah blah... # emp.manage_changeProperties(REQUEST) self._setObject(id, newShopper) I am hopeful that this will expose a glaring misconception of mine about propertysheets. ;-) Someone please tell me how stupid I am and what I'm missing here... This does me no good... since I have to create the propertysheets and I want to allow for properties that I *don't know about* (i.e., customizations that individual applications need to make these 'shoppers' work as 'clients' or 'memebrs' or whatever else...) Clearly I'm flailing here.. Any ideas from the wizards of Z? How would you modify addShopper to make it work with other applications and allow for multiple propertysheets that need to be set up from the addShopperForm.... Also.. I'm hoping for some advice about the old 'Basket' object. Basically the old basked object was a simple container, one for each shopper, that held 'Basket Items' which were essentially references to MarketItems, that were on sale in the store, along with quantity, options, etc.. How should I work these in with ZPatterns? Should there be a BasketManager for each shopper that is essentially a Specialist for BasketItems? Other ideas welcome... thanks, -steve
Hi Folks, Is there any reason why a Rack shouldn't be 'exportable'? I have a Rack of objects, but when I export/import (as a .zexp) and then call "getItem" I'm getting: <TD WIDTH="90%"> <H2>Zope Error</H2> <P>Zope has encountered an error while publishing this resource. </P> <P> <STRONG>Error Type: AttributeError</STRONG><BR> <STRONG>Error Value: _setRack</STRONG><BR> </P> <HR NOSHADE> <p><a href="http://www.zope.org/Credits" target="_top"><img src="http://jupiter.spvi.com:13080/p_/ZopeButton" width="115" height="50" border="0" alt="Powered by Zope"></a></p> </BODY></HTML> <PRE> Traceback (innermost last): File /usr/local/etc/Zope2a/lib/python/ZPublisher/Publish.py, line 222, in publish_module File /usr/local/etc/Zope2a/lib/python/ZPublisher/Publish.py, line 187, in publish File /usr/local/etc/Zope2a/lib/python/Zope/__init__.py, line 221, in zpublisher_exception_hook (Object: Transactional) File /usr/local/etc/Zope2a/lib/python/ZPublisher/Publish.py, line 171, in publish File /usr/local/etc/Zope2a/lib/python/ZPublisher/mapply.py, line 160, in mapply (Object: index_html) File /usr/local/etc/Zope2a/lib/python/ZPublisher/Publish.py, line 112, in call_object (Object: index_html) File /usr/local/etc/Zope2a/lib/python/OFS/DTMLMethod.py, line 167, in __call__ (Object: index_html) File /usr/local/etc/Zope2a/lib/python/DocumentTemplate/DT_String.py, line 502, in __call__ (Object: index_html) File /usr/local/etc/Zope2a/lib/python/DocumentTemplate/DT_In.py, line 691, in renderwob (Object: defaultRack.getPersistentItemIDs()) File /usr/local/etc/Zope2a/lib/python/DocumentTemplate/DT_Let.py, line 147, in render (Object: currID="_['sequence-item']") File /usr/local/etc/Zope2a/lib/python/DocumentTemplate/DT_With.py, line 133, in render (Object: getItem(currID)) File /usr/local/etc/Zope2a/lib/python/DocumentTemplate/DT_Util.py, line 337, in eval (Object: getItem(currID)) (Info: currID) File <string>, line 0, in ? File /usr/local/etc/Zope2a/lib/python/Products/ZPatterns/Specialists.py, line 34, in getItem (Object: Transactional) File /usr/local/etc/Zope2a/lib/python/Products/ZPatterns/Rack.py, line 60, in getItem (Object: Transactional) File /usr/local/etc/Zope2a/lib/python/Products/ZPatterns/Rack.py, line 117, in retrieveItem (Object: Transactional) File /usr/local/etc/Zope2a/lib/python/OFS/Uninstalled.py, line 110, in __getattr__ (Object: broken) AttributeError: (see above) </PRE> The wierd thing is it seems to work with some racks and not others.... -steve
At 10:04 AM 8/22/00 -0500, Steve Spicklemire wrote:
Moving this to ZPatterns brings up all sorts of questions. First of all I
no longer
know that the actual class stored my shopper specialist is a 'Shopper'. It could be any class kept in the shopper manager's rack(s). Soo... my addShopper should probably become:
def addShopper(self, id, REQUEST): """ add a new shopper... self is now the shopperManager (Specialist) """
newShopper = self.newItem( id )
newShopper.propertysheets.methodToUpdatePropertySheetsFromMappingObject( REQUEST ) Depending on how your framework specifies its requirements for a Shopper, you probably want something like: newShopper.propertysheets.get('shopperData').manage_changeProperties(...) or newShopper.manage_changeProperties(...) or if newShopper.propertysheets.manage_addPropertySheet(id='shopperData',ns=''): newShopper.propertysheets.get('shopperData').manage_changeProperties(...) else: raise 'Unable to add propertysheet' Notice that you must specifically ask for a particular sheet from the propertysheets object before trying to change it. Also note that unless you require users of your framework to make that sheet already available when a newItem() is created, you must add the sheet yourself.
Looking through VirtualSheets and PropertySheets code I see __propsets__ so I was thinking something like:
for prop in newShopper.propertysheets.__propsets__(): setattr(newShopper, prop, REQUEST.get(prop,None))
Don't do this. __propsets__ is a private method and should not be touched. If you must iterate through an object's propertysheets, use: for sheet in newShopper.propertysheets: ... Which will go through all of the object's property sheets, including default sheets such as the object's main WebDAV property set.
but this doesn't seem to work.... (I get 'nameError 'client'') in the debugger.... I have no clue where 'client' comes from, reminds me
This looks to me like a problem that someone else reported, and which I fixed, but it's not fixed in a released version. :( See below: Index: DataSkins.py =================================================================== RCS file: /u/idsuser/REPOSITORY/ZProducts/ZPatterns/DataSkins.py,v retrieving revision 1.49 retrieving revision 1.50 diff -r1.49 -r1.50 377c377 < l.extend(list(sp._PropertySheetsFor(client))) ---
l.extend(list(sp._PropertySheetsFor(self)))
of the 'DTML Method' argument called 'client'... but I don't see a traceback in the debugger.. anyway.. I've come to something like this, though it doesn't do what I need.... :
newShopper = self.newItem(id) newShopper.passwd = passwd
newShopper.propertysheets.manage_addPropertySheet(id='EMarketProps', ns='')
emp = newShopper.propertysheets.get('EMarketProps') emp.manage_addProperty('email', '', 'string') emp.manage_addProperty('name','','string') # # ... blah blah blah...
# emp.manage_changeProperties(REQUEST) self._setObject(id, newShopper)
Actually, this is the correct way to do it, if you are not requiring shopper objects to have this sheet predefined and available for you. My suggestion, though, is that you do just that (require the shopper objects to support what you need).
I am hopeful that this will expose a glaring misconception of mine about propertysheets. ;-) Someone please tell me how stupid I am and what I'm missing here... This does me no good... since I have to create the propertysheets and I want to allow for properties that I *don't know about* (i.e., customizations that individual applications need to make these 'shoppers' work as 'clients' or 'memebrs' or whatever else...) Clearly I'm flailing here..
I don't see that you are. I don't see any problems at all here.
Any ideas from the wizards of Z? How would you modify addShopper to make it work with other applications and allow for multiple propertysheets that need to be set up from the addShopperForm....
I wouldn't do that. Actually, I think I now see what your actual problem is. You're trying to implement a whitebox specialist as if it were a blackbox specialist. A whitebox shouldn't have an add function unless you want to make it possible for someone to use the whitebox without customization. If someone wants to have clients (for example), they should override your addShopperForm to redirect to an addClientForm in their Clients specialist, and similarly override the other functions of your Shoppers specialist to delegate to the Clients specialist.
Also.. I'm hoping for some advice about the old 'Basket' object. Basically the old basked object was a simple container, one for each shopper, that held 'Basket Items' which were essentially references to MarketItems, that were on sale in the store, along with quantity, options, etc.. How should I work these in with ZPatterns? Should there be a BasketManager for each shopper that is essentially a Specialist for BasketItems? Other ideas welcome...
I don't have a clear enough idea of your requirements to offer any real suggestions here. My reaction to the above is basically, "it depends". :) What do baskets *do*? What do basket items *do*? What are the essential characteristics of these things which will remain unchanged regardless of the end-user's application of the framework, versus the changeable characteristics that are the domain of the application, rather than the framework. An example of this sort of analysis happened for me at work recently; doing requirements gathering for an asset tracking system, it quickly became clear to me that there were at least 101 uses for such an animal which would have required it to store data which was not "essential" in the sense of being universal to all Asset objects. For example, shared hosting system administrators wanted to know servers' IP addresses, operating systems, and so on. Dedicated server management people wanted all that plus what customer was renting the box and whether they'd paid their bill. Before too long, it became clear that the most useful way to abstract all this was to allow each Asset to have a Purpose which it was used for. Purpose is a whitebox which can be mapped to a variety of specific objects for each kind of application as it is developed - a shared hosting server Purpose, a dedicated server Purpose, and so on. A unending variety of objects can be developed which implement the Purpose interface (which really only needs a description and a link, from the asset management framework's point of view). These Purposes may be accessed through a variety of Specialists, each customized with a UI and capabilities appropriate for its respective audience. Then, through the underlying database, asset data such as location, serial number, and cost can be retrieved. Conversely, examining an asset through the asset management specialists allows display of the description of its current purpose, and a use history reflecting when it was assigned to different purposes (again, descriptions only). The descriptions, however, can be rendered as links to the actual purpose objects, by way of their individual specialists, thus allowing the user to hop into any of several "vertical" applications tied into the framework. In the design on this project, we plan to actually implement a "purposes" table which will effectively contain nothing more than a key, a description, and data to be used to link to the "real" purpose object by way of the correct specialist. Then, depending on the individual application's needs, other tables may be created (e.g. dedicated_server_accounts or shared_hosting_servers) which share the same primary key. The "vertical" specialists will join from this table to purpose and from there to the asset(s) tied to that purpose. Going the other way, the asset management system can only join as far as the purposes table, since it will (should) not need to know how every possible purpose is implemented. The asset management framework itself will not change as new purposes for assets are discovered and features to support them are built into the overall application. This decoupling should allow rapid, parallel development of new features and subsystems once the core framework is functional. Anyway, sorry to babble, but I thought it might be helpful to show an actual real-life example of framework development with ZPatterns that might have some principles applicable to your project.
OK.. I think I've found a way to do what I want with propertysheets. I just add a generic attribute provider, or skin script to grab the attributes out of the propertysheet... SkinScript to get property from particular property sheet: WITH self.propertysheets.get('ShopperData') COMPUTE email=getProperty('email'), address=getProperty('address') I'll march ahead with this plan until someone tells me the much easier way that I completely missed.. ;-) thanks, -steve
participants (2)
-
Phillip J. Eby -
Steve Spicklemire