Acquisition not working as expected
Hello, sorry to come up with this again. I startet 2 pretty confused threads on the same problem allready: [Zope] Error Value: 'File' object has no attribute 'manage_fixupOwnershipAfterAdd' Re: [Zope] context in fs product Unfortunatly I'm still not getting it right. I implemeted the good advices I got in the 2 Threads, slept over it a couple of nights. Looked at the code again, cleaned it up ... still: no success. Here is what I'm doing: I'm about to write a fs product to report on data generated by the "grinder load test tool" The load tests produce various log files and sar outputs that will have to be parsed and imported into Zope. The instance of the product is created: def manage_addZLTMS(self,id,title='',REQUEST=None,submit=None): "Add a ZLMTS to a folder." id=id.replace(' ','_') zltmsObj=ZLTMS(id,title) id=self._setObject(id, zltmsObj) folder=getattr(self, id) folder.manage_addFolder(id='LoadTests',title='Collection') The LoadTest folder is the place where the imported data will be stored. The base class ... class ZLTMS(Lasttest,Implicit, Persistent, PropertyManager, ObjectManager, SimpleItem): """ZLMTS object""" ... has an import method def newLasttest(self, REQUEST=None): """ new """ ltc=getattr(self,'LoadTests') ltid = self.genId(ltc) lt=Lasttest() lt.id=ltid ltc._setObject(ltid,lt) self.lt = getattr(ltc,ltid) self.lt.importData(self.manage_targets) This generates and instance of the Lasttest() class and imports the data. Lasttest() subclasses from Folder() and store all imported data in a folder hirarchy under itself. This all works fine and I can browse the imported data in the ZMI. The problems start when I want to access the data. eg.: In the base class I created a test3() method: def test3(self): """dsaf""" return self.getId() I can call this method on whatever folder inside the product instance I want, it will always return the id of the product and not the id of the product I call it on. Why is that? So I thought for some reasons the objects in the folder hirarchy are not acquisition wrapped. So I created another testspace() method in the base class: def testspace(self,REQUEST=None): """tests""" if hasattr(self, 'aq_base'): return 'aq wraped' return 'not aq wraped' I can call this on any object in the folder hirarchy and it returns: 'aq wrapped'. Can so. please help me get this straight or hint towards further analysis that I can do. Thanks in advance. Greetings Roman BTW: I did not post any code of the Lattest() class since the problem is allready present in the folder that holds its instances. Of course I will post any part of the code you want me to.
+-------[ Roman Klesel ]---------------------- | Hello, | class ZLTMS(Lasttest,Implicit, Persistent, PropertyManager, ObjectManager, SimpleItem): | """ZLMTS object""" | | ... has an import method | | def newLasttest(self, REQUEST=None): | """ new """ | ltc=getattr(self,'LoadTests') | ltid = self.genId(ltc) | lt=Lasttest() | lt.id=ltid | ltc._setObject(ltid,lt) | self.lt = getattr(ltc,ltid) | self.lt.importData(self.manage_targets) | | This generates and instance of the Lasttest() class and imports the data. | Lasttest() subclasses from Folder() and store all imported data in a folder hirarchy under itself. | | This all works fine and I can browse the imported data in the ZMI. The problems start when I want to access the data. | | eg.: | | In the base class I created a test3() method: | | def test3(self): | """dsaf""" | return self.getId() | | I can call this method on whatever folder inside the product instance I want, it will always return the id of the | product and not the id of the product I call it on. | | Why is that? Are you sure your genId() method works? what does return self.id return instead of self.getId() ? -- Andrew Milton akm@theinternet.com.au
Hello Andrew, Andrew Milton schrieb:
Are you sure your genId() method works?
Well, yes, it does what it's ment to do. It generates an id, a string. Here it is: def genId(self,context): "asdf" items = [ int(e[2:]) for e in context.objectIds('Folder') ] if items == []: return 'lt100000' return 'lt%s' % str(max(items)+1
what does return self.id return instead of self.getId() ?
I just checked, there is no difference. Both return the id of the product instance as string. Greetings Roman
Roman Klesel wrote at 2006-3-1 13:24 +0100:
... I can call this method on whatever folder inside the product instance I want, it will always return the id of the product and not the id of the product I call it on.
I fear you will need to more clearly describe what you mean with "the product" (on one hand) and "the product I call it on" (on the other hand). You should clearly state which classes the objects "the product" and "the product I call it on" have (classes are the primary influence, the acquisition relation is the secondary influence -- this should should state clearly as well). Note also that objects in your Zope hiearchy are not "products" (a product is a Zope extension mechanisms usually defining and registering various classes that can be use to create persistent Zope objects). -- Dieter
Dieter Maurer schrieb:
I can call this method on whatever folder inside the product instance I want, it will always return the id of the product and not the id of the product I call it on.
I fear you will need to more clearly describe what you mean with "the product" (on one hand) and "the product I call it on" (on the other hand).
Yes sorry I got this wrong. The sentence should be like that: I can call this method on whatever folder inside the product instance (instance of the base class ZLTMS) I want, it will always return the id of the instace of the base class (where the method is defined) and not the id of the object (Folder object or File object) I call it on. Once again: The module that holds the base class looks like this: __doc__="""Das ist ZLMTS""" __version__='0.1' from Globals import InitializeClass, Persistent from Products.PageTemplates.PageTemplateFile import PageTemplateFile from AccessControl import ClassSecurityInfo from AccessControl.Role import RoleManager from Acquisition import Implicit from OFS.SimpleItem import SimpleItem from OFS.PropertyManager import PropertyManager from OFS.ObjectManager import ObjectManager from OFS.Folder import Folder from Lasttest import Lasttest manage_addZLTMSForm=PageTemplateFile('zpt/manage_addZLTMSForm',globals()) def manage_addZLTMS(self,id,title='',REQUEST=None,submit=None): "Add a ZLMTS to a folder." id=id.replace(' ','_') #Jetzt wird das Product als Instanz erzeugt ... zltmsObj=ZLTMS(id,title) #... und persitiert id=self._setObject(id, zltmsObj) folder=getattr(self, id) #Create a container for the Loadtests folder.manage_addFolder(id='LoadTests',title='Collection') #irgned ein WODOO falls jemand das PRoduct programmatisch hinzufuegt. if REQUEST is not None: try: destURL=self.DestinationURL() except: destURL=REQUEST['URL1'] REQUEST.RESPONSE.redirect(destURL+'/manage_main') return '' class ZLTMS(Lasttest,Implicit, Persistent, PropertyManager, ObjectManager, SimpleItem): """ZLMTS object""" security = ClassSecurityInfo() meta_type = 'ZLMTS' manage_options = ObjectManager.manage_options + ( {'label' : 'View', 'action' : ''}, {'label' : 'Settings', 'action' : 'manage_editSettingsForm'}, ) + PropertyManager.manage_options + SimpleItem.manage_options _properties=({'id':'title','type':'string','mode':'w'}, {'id':'description','type':'text','mode':'w'}, ) def __init__(self, id, title='', description='' ): "initialise a new instance of ZLMTS" self.id=id self.title=title self.description=description self.location_types=['http','path'] self.manage_targets={'gr_sys' : {'name' : '', 'phy_mem' : '', 'location_sar' : '', 'grinder_home' : '', 'server_start_url' : ''}, 'sys1' : {'name' : '', 'phy_mem' : '', 'location_sar' : '', 'server_start_url' : ''}} and so on ...
Roman Klesel wrote at 2006-3-2 08:13 +0100:
... I can call this method on whatever folder inside the product instance (instance of the base class ZLTMS) I want, it will always return the id of the instace of the base class (where the method is defined) and not the id of the object (Folder object or File object)
This is how acquistion works (and should work): When you acquire a method, the "self" this method is called with is usually *NOT* the object acquisition is applied on. If you think about it, you will recognize that this must be the case: For any method "m" of class "C", its first argument *MUST* be a "C" instance. If you acquire the method "m" starting from "o", then "o" may have a completely different class then "m". In your case, the method "test3" is not defined by "Folder" but by what you call "your product instance" (still a wrong term!). Therefore, its "self" is necessarily an instance of "your product" (I keep your wrong term). And "self.getId()" gives (consequently) its (and not the Folder's) id. You cannot do with acquisition what you want to do... -- Dieter
Dieter Maurer schrieb:
Roman Klesel wrote at 2006-3-2 08:13 +0100:
This is how acquistion works (and should work):
When you acquire a method, the "self" this method is called with is usually *NOT* the object acquisition is applied on.
If you think about it, you will recognize that this must be the case:
For any method "m" of class "C", its first argument *MUST* be a "C" instance.
If you acquire the method "m" starting from "o", then "o" may have a completely different class then "m".
OK
In your case, the method "test3" is not defined by "Folder" but by what you call "your product instance" (still a wrong term!). Therefore, its "self" is necessarily an instance of "your product" (I keep your wrong term). And "self.getId()" gives (consequently) its (and not the Folder's) id.
What would then be the right term? (I suspect I'll have to discuss this further)
You cannot do with acquisition what you want to do...
I hope I will. However I might have to do it in a different way. Thanks, this helped me to be more clear about acquisition. Roman
Roman Klesel wrote at 2006-3-3 08:20 +0100:
...
In your case, the method "test3" is not defined by "Folder" but by what you call "your product instance" (still a wrong term!). Therefore, its "self" is necessarily an instance of "your product" (I keep your wrong term). And "self.getId()" gives (consequently) its (and not the Folder's) id.
What would then be the right term? (I suspect I'll have to discuss this further)
An instance of my class defining "test3". Note again: A product is an extension mechanism for Zope usually defining and registering various classes to be instantiated as (what I call) site building objects. Thus, there is no product instance (in your Zope hierarchy). Instead you have instances of classes defined in your product. I would give the classes articifical names, say "C", "C1", "C2" ... E.g. a perfect description of your problem could have been: In a product I defined a class "C" deriving from "Folder" with method "test3" which prints "self.getId()". I can call "test3" (via acquistion) on objects contained in an instance "I" of "C". However, the printed id is always the id of "I" and not that of the object on which "test3" has been called on. -- Dieter
Hello, Roman Klesel schrieb:
I can call this method on whatever folder inside the product instance I want, it will always return the id of the product and not the id of the product I call it on.
In order to make my problem more obvious I created a minimal product. See attachmet. It displays exactly the problem I'm facing: A method in the base class that returns self.id, returns the id of the base class on all subfolders it is called. Code: __doc__="""This is Minimal""" __version__='0.1' from Globals import InitializeClass, Persistent from Products.PageTemplates.PageTemplateFile import PageTemplateFile from AccessControl import ClassSecurityInfo from Acquisition import Implicit from OFS.ObjectManager import ObjectManager from OFS.SimpleItem import SimpleItem import OFS class minimal(Persistent,Implicit,ObjectManager,SimpleItem): "minimal object" security = ClassSecurityInfo() meta_type = 'minimal' def __init__(self, id): "initialise a new instance of Minimal" self.id = id index_html=PageTemplateFile('zpt/index.html',globals()) security.declarePublic('sayHello') def sayHello(self): """just says hello""" return 'Hello' def showId(self): return self.id def manage_addMinimal(self, REQUEST=None): "Add a Minimal to a folder." self._setObject('minimal_id', minimal('minimal_id')) min_obj = getattr(self,'minimal_id') min_obj.manage_addFolder('tfolder') if REQUEST is not None: try: destURL=self.DestinationURL() except: destURL=REQUEST['URL1'] REQUEST.RESPONSE.redirect(destURL+'/manage_main') return '' InitializeClass(minimal) Any ideas anyone? Greetings Roman
+-------[ Roman Klesel ]---------------------- | Hello, | | Roman Klesel schrieb: | > I can call this method on whatever folder inside the product instance I want, it will always return the id of the | > product and not the id of the product I call it on. | | In order to make my problem more obvious I created a minimal product. See attachmet. | | It displays exactly the problem I'm facing: | | A method in the base class that returns self.id, returns the id of the base class on all subfolders it is called. That's the way acquisition works. I'm not sure why you want this, since everything you're adding has an id already, so there's no need to acquire any method to get the id. Perhaps if you describe the problem you're trying to solve, we can actually help you. -- Andrew Milton akm@theinternet.com.au
Andrew Milton schrieb:
Perhaps if you describe the problem you're trying to solve, we can actually help you.
Yes, thank you! I just want to be able to do what I'm used to do from TTW: - There is a hirarchy of Folder/File objects - In the root directory there is an python script (showData.py) - the python script operates on context eg. return context.data
From a ZPT I can now loop over a Folder and call the python script on every object in the folder:
<ul> <li tal:repeat="elem context/objectValues" tal:content="elem/showData">data</li> <ul> Depending on the URL I call this on I will get a list of data of all files present here. This is just what I want to do in the fs product I'm writing: I have a folderish object that holds a collection of File objects nicely sorted in a hirarchy of Folders objects. This folderish object has methods to display the data in the File objects (underneth it self) in differnt ways eg. def genGraph(self): "plot graph from self.data and return a png image" ... def getDataMatrix(self): "return self.data as a list of rows where every field is a list element" ... just as in my example above I want to be able to call this methods on any File objects from a ZPT like this: <ul> <li tal:repeat="elem context/objectValues" <img tal:attributes="src string: ${elem/absolute_url}/genGraph"> plot </img> </li> <ul> Therefore I started the thread: [Zope] context in fs product The answer I got: 'self' inside the a Zope product is the _same_ as 'context' or 'here' within ZPT or PythonScripts. So now I'm stuck. Of course I could pass the object or its id to the methods as an argument eg. <span tal:define="data python:context.genGraphs(elem)">sdf</span> and in the method get the the object with getattr() or something ... But this wouldn't be nearly as nice and I'd like to avoid this. (The code examples here might have errors, I just typed them in as an illustration of my thougts) Greetings Roman
+-------[ Roman Klesel ]---------------------- | Andrew Milton schrieb: | > | > Perhaps if you describe the problem you're trying to solve, we can actually | > help you. | > | | Yes, thank you! | just as in my example above I want to be able to call this methods on any File objects from a ZPT like this: | | <ul> | <li tal:repeat="elem context/objectValues" | <img tal:attributes="src string: ${elem/absolute_url}/genGraph"> plot </img> | </li> | <ul> In what way doesn't this work? I've certainly used this pattern in my FS products without problems. | 'self' inside the a Zope product is the _same_ as 'context' or 'here' within | ZPT or PythonScripts. It IS in the context of that object. However, if you call a method that is acquired, self in that method is the acquired object, not the acquiring object. -- Andrew Milton akm@theinternet.com.au
Andrew Milton schrieb:
| <ul> | <li tal:repeat="elem context/objectValues" | <img tal:attributes="src string: ${elem/absolute_url}/genGraph"> plot </img> | </li> | <ul>
In what way doesn't this work? I've certainly used this pattern in my FS products without problems.
Hmm? Did I miss something? A few posts above we recoginzed that when I would do: def genGraph(self): "bla" return self.data then genGraph would return data from the class where it is defined and not from the object acquiring it. So it would fail.
| 'self' inside the a Zope product is the _same_ as 'context' or 'here' within | ZPT or PythonScripts.
It IS in the context of that object. However, if you call a method that is acquired, self in that method is the acquired object, not the acquiring object.
Yes, this is what I understood (at least I think so) now. So the question is: How will the method find out what object I want it to operate on if I call it in this way context/genGraphs or objref/genGraphs I actually want it to operate on the object I called it on, but this is not what self inside the method represents. Roman
+-------[ Roman Klesel ]---------------------- | Andrew Milton schrieb: | > | <ul> | > | <li tal:repeat="elem context/objectValues" | > | <img tal:attributes="src string: ${elem/absolute_url}/genGraph"> plot </img> | > | </li> | > | <ul> | > | > In what way doesn't this work? I've certainly used this pattern in my FS | > products without problems. | > | | Hmm? Did I miss something? A few posts above we recoginzed that when I would do: | | def genGraph(self): | "bla" | return self.data | | then genGraph would return data from the class where it is defined and not from the object acquiring it. So it would fail. How about you forget about Acquisition for now. Define the method in the class you want to call it on. Once you're happy with the way that works, you can use Acquisition to keep things like state or configuration for a particular path. If you CAN'T define the method in the class (not sure why you couldn't), then you will have to define a method and pass an instance or a reference. -- Andrew Milton akm@theinternet.com.au
Andrew Milton schrieb:
How about you forget about Acquisition for now.
Well, hmmm ... this will be hard ... I was just about to fall in love with it ...
Define the method in the class you want to call it on.
Then I will have to subclass from OFS.Image.File and I was told that this is somehow evil. From a previous thread: Roman Klesel wrote:
form OFS.Image import File
- I build a class _File(File):
I really doubt you need to do that...
- pimped it up a little bit.
What, specifically, did you add?
- _setObject'ed it
You shouldn't be calling that directly...
If you CAN'T define the method in the class (not sure why you couldn't), then you will have to define a method and pass an instance or a reference.
This makes the code in the ZPT all messy... The third option is to somehow construct the the object the method should operate on from self.REQUEST. I think I will try that ... I was really hoping to get to understand this acquisition thing. Am I really that far away from getting it? What does it take to grasp it? Thanks anyway for your time and efforts! Roman
+-------[ Roman Klesel ]---------------------- | Andrew Milton schrieb: | > How about you forget about Acquisition for now. | > | | Well, hmmm ... this will be hard ... I was just about to fall in love with it ... It's definitely useful, but, you have to pick your moments to use it. | > Define the method in the class you want to call it on. | | Then I will have to subclass from OFS.Image.File and I was told that this is somehow evil. From a previous thread: | | Roman Klesel wrote: | >> form OFS.Image import File | >> | >> - I build a class _File(File): | | >I really doubt you need to do that... | | >> - pimped it up a little bit. | | >What, specifically, did you add? | | >> - _setObject'ed it | | >You shouldn't be calling that directly... Well you have to decide who to believe then won't you ? d8) | I was really hoping to get to understand this acquisition thing. | Am I really that far away from getting it? What does it take to grasp it? Think of Acquisition this way; read-only, run-time, inheritance. -- Andrew Milton akm@theinternet.com.au
Roman Klesel wrote at 2006-3-3 10:26 +0100:
... Yes, this is what I understood (at least I think so) now. So the question is:
How will the method find out what object I want it to operate on if I call it in this way
context/genGraphs or objref/genGraphs
It cannot. It works for a PythonScript because in this case you acquire an object (and not a method) and then call its method ("__call__"). The difference is that an object is acquired, not a method. You can try to arrange things this way even with classes defined in products: You define a new class "WC", derived from "Implicit". Its "__call__" method does, what you would like to happen. In "__call__", you access the acquisition context with "self.aq_parent". In your class "C", you instantiate "WC": test3 = WC() You can then acquire "test3" (it now is an object, no longer a method) and call it. In "test3.__call__", "self.aq_parent" will be the object, from which you acquired "test3". -- Dieter
participants (3)
-
Andrew Milton -
Dieter Maurer -
Roman Klesel