Hi Phillip.... First of all... please forgive the long message. When I started it was going to be a quick note... now it's turned into a bit of a novel... but you don't have to read it all. ;-> I'm spewing all this out in part to illustrate some of the things I've done 'wrong' so that others might not go down that road... and also... your reaction (if any) to some of the random thoughts popping up here and there would be most welcome... anyway.. here goes.... OK.. I think this makes sense.. (hmm.. how's *that* for a statement of supreme confidence!)
"pje" == Phillip J Eby <pje@telecommunity.com> writes:
pje> As for set/get methods, we usually just define DataSkin pje> property sheets in the ZClass, and use permission mappings to pje> define the read/write permissions for each sheet. We group pje> properties in sheets according to the domain model pje> permissions for access. Then the domain logic methods in the pje> ZClass call propertysheets.sheetname.manage_changeProperties() with pje> keyword parameters to set things. Usually, we have domain pje> specific methods that alter the values of multiple attributes pje> at the same time, using the property sheets. We rarely have pje> code outside of the ZClass directly manipulate attributes. pje> In instances where we've needed to set attributes which are pje> of types not supported by Zope propertysheets, we've had to pje> use ExternalMethods or something similar. In the long run we pje> expect to be able to use PropertyHandlers to do this without pje> dropping down to Python. OK.. let me just make sure I'm understanding you correctly. You're not using propertysheet providers, but DataSkin propertysheets 'installed' in a ZClass that directly inherits from DataSkin. Your use of the "Data Skin Attribute Property Sheets" is primarily to associate permissions with groupings of attributes, and to simplify establishment and management of those properties through the ZClass administrative interface. As a bonus, you get 'free' methods needed to adjust these attributes from DTML (i.e., manage_changeProperties) without requiring custom PropertyHandlers. I did notice that when I create a ZClass (in a completely different project) that inherits from a Python Product (registered with registerBaseClass) that inherits from DataSkin, I don't get the option of a DataSkin propertysheet, in the 'common' container of the ZClass, so there's probably some manual fiddling I need to do when I register my ZClass base class that I'm not doing... I'm guessing that the code that defines the SkinZISS, SZHolder, and _ZClass_for_DataSkin culminating in registerZClass perform the necessary magic to make that happen.... Aha! I just found part of the magic in "ProductContext.py" lib/python/App/ProductContext.py This is deep magic! But I think I see that you are up to:
From DataSkins.py:
class SZHolder: """ """ def __init__(self): ZClassSheets.__init__.im_func(self) self.common=SkinZISS('common') class _ZClass_for_DataSkin: propertysheets = SZHolder() _zclass_ = DataSkin manage_options = () def initialize(context): context.registerZClass(_ZClass_for_DataSkin) So you set up SZHolder to pose as a 'propertysheets' attribute of the ZClass (I'm guessing that im_func(self) is to get around the 'unbound method' ExtensionClass problem.). Then your 'hand creating' the _ZClass_for_DataSkin rather than using 'registerBaseClass' You're also overriding the ZClass 'common' collection of propertysheets with one that will allow both 'regular' ZClass propertysheets, and also Dataskin propertysheets. Sooo... if I want my DataSkin subclass to have the same ability I'll need to do something similar? (just replace _zclass_ = DataSkin with _zclass_ = MyDataSkinSubclass?) This of course begs the question... why do I want to subclass from anything other than DataSkin in the first place? Well.. my *new* strategy (I'm using this on EMarket) is to put all the core logic in a Specialist subclass... and let my DataSkins be ZClasses that are directly subclassed from __ZClass_for_DataSkin. This has its own problems (I still have a hard time working in a 'specialist' on a method that is supposed to be acquired by a DataSkin, and keeping track of who 'self' is and what properties I can really get to etc...). But in my last project I hadn't figured all that out, so I originally made a layered system something like this: Thing.py ---------------------------------------------------------------------- try: # # if we're running in Zope.. this will work and we can inherit # from OFS.Folder # from OFS.Folder import Folder from Products.ZPatterns.DataSkins import DataSkin class FlexBase( DataSkin, Folder ): pass except: # # if we're testing stand-alone... we can just subclass from an empty class # class FlexBase: def __init__(self, id=None): self.id = id class Thing(FlexBase): """ Thing is a container that keeps track of all the local data we need about a thing. Instance data includes things like... """ id = None # id of thing title = '' def __init__(self, id=None): """ Create a new Thing instance. id is a (presumably contextually unique) identifier.. name is a human identifiable name """ FlexBase.__init__(self, id) def ... all the 'core logic' methods that don't use 'zope' directly..... if __name__=='__main__': run a bunch of test code.... ---------------------------------------------------------------------- Then I have ZThingBase.py ---------------------------------------------------------------------- import ..... class ZThingBase(Thing.Thing, Persistent, Implicit): """ Base Thing class for Zope. Starting with Thing add the features for the Zope management interface and other Zope specific bahaviors. """ meta_type = "ZThingBase" icon='misc_/ZThingBase/thing.gif' # # define the 'simple properties' for this object... # _properties = ( {'id':'title', 'type':'string', 'mode':'w'}, ) manage_options = ({'label':'CoreThing Properties', 'action':'manage_editForm'}, ) def __init__(self,id,title=''): Thing.Thing.__init__(self, id=id) manage_editForm = HTMLFile('manage_editForm', globals()) def manage_edit(self, REQUEST=None, RESPONSE=None): """ change the custom properties of the thing """ message = "Thing object changed... " + time.ctime(time.time()) ... edit core thing properties, whatever that means .. here... if RESPONSE is not None: return self.manage_editForm(self, REQUEST, manage_tabs_message = message) Globals.default__class_init__(ZThingBase) ---------------------------------------------------------------------- and finally in __init__.py ---------------------------------------------------------------------- def initialize(context): context.registerBaseClass(IBC_ZThingBase.IBC_ZThingBase) ..etc etc.. ---------------------------------------------------------------------- Why did I do all that? I am used to a development routine where I work a while... in emacs.. then hit ctrl-c ctrl-c which 'runs' the current module. With this setup, everything in Thing.py can work without Zope. All my core logic can be tested and I can verify that it works correctly *before* I layer all the ZClass stuff on top. (I know.. now I guess I need to learn to use ZPublisher/Client.py ;->) Of course this requires that my ZClasses are subclasses of my core logic base class.. and I now understand why that is generally undesirable. (It makes integrating frameworks later more difficult.... ) But there is a clear advantage... I think that with the flexibility of Racks and SkinScript you could probably even have the best of all possible worlds... anyway as usual.. thanks, -steve