Re: [Zope-dev] Success! Was: Re: [Zope-dev] How is 'retrieveItem intended to work with TTW Specialists? Specialists?
Yes.. I see what you mean... this is a 'whitebox customization' that I used as part of the integration of my framework into the 'online catalog' system. This is not 'blackbox' code that is integral to my system.... In fact, I mis-spoke earlier, it's not in my specialist at all... it only appears in a method of a ZClass that I use as the 'storage class' for objects controlled by one of my specialists.... this is clearly whitebox territory, since this wrapper is chosen by the integrator... The first thing my docs (will) say to do.. is go through all my specialists and choose 'storage' classes for each specialist. This brings up another point.... If I choose 'DataSkin' as a storage class, then my code hits the security system every time a refer to any data (simple types) stored directly in the DataSkin. I can either set __allow_access_to_unprotected_subobjects=1 on each instance, or I can write accessor methods for each attribute, (yuk!, but maybe good in some ways...) or I can ask the integrator to create 'dummy' ZClass(es) even for those racks that they don't want/need to customize (which seems to 'get around' the security system.) I'm curious to know what you folks do here.. do you write accessor methods for every type stored in your DataSkins? Anyway.. thanks for the great tools! I'm having fun now. ;-) -steve
"Phillip" == Phillip J Eby <pje@telecommunity.com> writes:
Phillip> At 09:15 PM 10/4/00 -0500, Steve Spicklemire wrote: >> This was also nice since in my Specialist I could do things >> like: >> >> <dtml-call >> "RESPONSE.redirect(this().theRealThing.absolute_url())"> >> >> After the user is finished working in my 'app area' they could >> get redirected back to the actual object that my Specialist was >> 'posing' for... Phillip> I don't really recommend the above, unless it's being Phillip> done by SkinScript. Otherwise, you're unnecessarily Phillip> exposing an implementation attribute (theRealThing). Phillip> Instead, require that your object implement a redirect() Phillip> method, and define it in the class as Phillip> "class_default_for_redirect". Then you can override the Phillip> method in SkinScript or with a class extender (a new Phillip> 0.4.3 feature). Doing it this way means your framework Phillip> code will not incorporate an assumption that your Phillip> whitebox specialist is always going to be wrapping Phillip> someone else's objects.
At 06:57 AM 10/5/00 -0500, Steve Spicklemire wrote:
This brings up another point.... If I choose 'DataSkin' as a storage class, then my code hits the security system every time a refer to any data (simple types) stored directly in the DataSkin. I can either set __allow_access_to_unprotected_subobjects=1 on each instance, or I can write accessor methods for each attribute, (yuk!, but maybe good in some ways...) or I can ask the integrator to create 'dummy' ZClass(es) even for those racks that they don't want/need to customize (which seems to 'get around' the security system.) I'm curious to know what you folks do here.. do you write accessor methods for every type stored in your DataSkins?
We never use raw DataSkins, first off. We always create a domain-specific ZClass subclassed from DataSkin. In your case, I would suggest simply including such ZClasses with your product to provide the default implementation of your domain and presentation logic. As for set/get methods, we usually just define DataSkin property sheets in the ZClass, and use permission mappings to define the read/write permissions for each sheet. We group properties in sheets according to the domain model permissions for access. Then the domain logic methods in the ZClass call propertysheets.sheetname.manage_changeProperties() with keyword parameters to set things. Usually, we have domain specific methods that alter the values of multiple attributes at the same time, using the property sheets. We rarely have code outside of the ZClass directly manipulate attributes. In instances where we've needed to set attributes which are of types not supported by Zope propertysheets, we've had to use ExternalMethods or something similar. In the long run we expect to be able to use PropertyHandlers to do this without dropping down to Python.
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
At 02:24 PM 10/5/00 -0500, Steve Spicklemire wrote:
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.
Yes.
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...
A simple way to avoid this is to simply create the ZClass subclassed from DataSkin *and* your Python class.
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....
Yes. If you want to make this work on a Python class, you need to do something like: class _ZClass_for_MyClass(_ZClass_for_DataSkin): _zclass_ = MyClass And then in your registration, call: context.registerZClass(_ZClass_for_MyClass) That should be it.
This is deep magic!
Aye. Here there be dragons. :) Wait'll you see the magic I had to do in 0.4.3 to get ZClass PlugIns and PlugInContainers to work...
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?)
See above for the shortest way to do this.
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...).
Personally, I don't recommend this approach, even if you end up with a ZClass that doesn't do much besides have a propertysheet or two.
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...
It is *not* undesirable to have ZClasses be subclasses of your core logic base class. Just make sure they *also* subclass DataSkin, or that you use the metaclass definition stuff above. You've actually got a pretty decent approach going there. One question, though. Why the derivation from Folder?
participants (2)
-
Phillip J. Eby -
Steve Spicklemire