[Zope-dev] Accessors for DataSkin attributes (was Re: [Zope-dev] Success!

Steve Spicklemire steve@spvi.com
Thu, 5 Oct 2000 14:24:00 -0500 (EST)


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