[Zope-dev] Trouble setting LoginManager default user class

Phillip J. Eby pje@telecommunity.com
Wed, 17 May 2000 16:33:23 -0500


At 03:18 PM 5/17/00 -0400, Dan L. Pierson wrote:
>I'm trying to port the PTK PersistentUserSource stuff to the new
>LoginManager.  A lot of things seem to be working, but I seem to have
>fallen into a Catch 22:
>
>1. UserSources.BasicUserSource defines self._defaultClass as LoginUser, where
>UserSources.LoginUser is a simple RackMountable class (not a ZClass)
>defined earlier in the file.
>
>2. PTK requires DemoPortal.LoginMember, a ZClass that inherits from
>LoginUser and PersistentUserSource.MemberMixin (actually, it's having
>the MemberMixin stuff that's crucial).
>
>3. Rack._v_itemConstructor is a ComputedAttribute that looks at
>self._defaultClass: if it's a class, it uses it, if it's a string it
>looks in Products.meta_classes[self._defaultClass] for the class to
>use.

It uses:

getattr(self, '_zclass', None) or self._defaultClass

So as to look for a self._zclass attribute first.  _defaultClass is a
fallback.


>So PersistentUserSource needs to change _defaultClass to refer to
>DemoPortal.LoginMember.  If I try to do it by using a
>ComputedAttribute:
>
>    def _defaultClass(self):
>        c = Products.DemoPortal.LoginMember
>        self._defaultClass = c
>    
>    _defaultClass = ComputedAttribute(_defaultClass)

Don't use a computed attribute; it won't help you as computed attributes
run without acquisition context.  This is why _v_defaultItemConstructor
wants to have either an actual ZClass reference, or a string that can be
looked up in the Python-level registries.


>I get an error traceback from an AttributeError in Rack.createItem for
>_v_itemConstructor.  BTW: this also happens if I also define
>_v_itemConstructor as a ComputedAttribute in PersistentUserSource.  I
>need something like ComputedAttributes here, because DemoPortal
>doesn't (necessarily?) exist at the time PTKDemo is installed.

Oops.  Well, that could be a problem.  Here's a question, though.  Why does
the default class need to be LoginMember?  Can't you just set that when you
actually create the UserSource?


>If I try to do it by setting _defaultClass to 'LoginMember', it fails
>because 'LoginMember' isn't in Products.meta_classes.  In fact nothing
>relating to DemoPortal or any other ZClass product is in

>Products.meta_classes!  The official way to get into that dictionary
>appears to be to call registerBaseClass or registerZClass.
>registerZClass would seem like the correct method, but the ONLY call
>of this is in Products/OFSP/__init__.py for
>ZClasses.ObjectManager.ZObjectManager.  Should ZClass products be
registering 
>themselves?

This is a limitation of using ComputedAttribute for
_v_defaultItemConstructor, which is why manage_setStorage looks up the
ZClass in the main ZClass registry, and then sets _zclass to point to it.


>If I try to set _defaultClass in PersistentUserSource.__init__. I
>can't figure out how to refer to
>/Control_Panel/Products/DemoPortal/LoginMember from there.

You can't do it from __init__, and you don't want to, anyway, because
__init__ doesn't have acquisition context.  It needs to be in
manage_afterAdd and it needs to check whether self._zclass is already set.
If not set, then it should call self.manage_setStorage(zclass='meta type
you want').  Something like:

    def manage_afterAdd(self,item,container):
        UserSource.manage_afterAdd(self,item,container)
        if not hasattr(self,'_zclass'):
            self.manage_setStorage(zclass='LoginMember')

The above assumes the meta_type is 'LoginMember'; you should change it if
appropriate.

Sorry for your pain; the _v_itemConstructor is a bit of a hack.  I could
probably fix this by double-layering ComputedAttributes so that it has
acquisition context when it maps from a string to a ZClass.

[pause as I hack, implement, test...]

Here's a patch that works for me (as in it doesn't break other uses of
Rack, I don't know if it'll fix your problem).  Try setting _defaultClass
to the string representing the meta_type you want, and see if it works.
The patch changes things to use a double-layered ComputedAttribute so
_v_itemConstructor is called in acquisition context.  It also now only sets
self._zclass to a meta_type, instead of referencing the ZClass directly.
The combination should make it possible to use a string for _defaultClass
that refers to a ZClass which is not registered from Python.  If it doesn't
work, try my other suggestion(s) above, but this is probably the best way
to fix it.


Index: Rack.py
===================================================================
RCS file: /u/idsuser/REPOSITORY/ZProducts/ZPatterns/Rack.py,v
retrieving revision 1.45
retrieving revision 1.46
diff -u -r1.45 -r1.46
--- Rack.py	2000/05/05 16:44:24	1.45
+++ Rack.py	2000/05/17 21:20:24	1.46
@@ -185,7 +185,7 @@
 
         if c:
             if type(c) is type(''):
-                c = Products.meta_classes[c]
+                c = self._unifiedZClassRegistry()[c][1]
#Products.meta_classes[c]
         else:
             def err(key):
                 raise TypeError, "No ZClass set - please use 'Storage' tab"
@@ -195,9 +195,8 @@
         self._v_itemConstructor = c
         return c
 
-    _v_itemConstructor = ComputedAttribute(_v_itemConstructor)
+    _v_itemConstructor = ComputedAttribute(lambda
s,v=ComputedAttribute(_v_itemConstructor): v)  
 
-
     def _RawItem(self, key):
         """
         Create an empty object of the right class
@@ -346,12 +345,12 @@
         """
         if zclass is not None:
             if self._unifiedZClassRegistry().has_key(zclass):
-                if Products.meta_classes.has_key(zclass):
+                #if Products.meta_classes.has_key(zclass):
                     self._zclass = zclass
-                else:
-                    self._zclass = self._unifiedZClassRegistry()[zclass][1]
-                if hasattr(self,'_v_itemConstructor'):
-                    del self._v_itemConstructor
+                #else:
+                #    self._zclass = self._unifiedZClassRegistry()[zclass][1]
+                    if self.__dict__.has_key('_v_itemConstructor'):
+                         del self._v_itemConstructor
             else:
                 raise NameError,("Invalid/nonexistent ZClass '%s'" % zclass)