Hi, I'm trying my hands on a Zope product. The different `views' are to be rendered with the help of dtml files, thus the class definition looks something like: |class CMSResource( Traversable.Traversable, ...) | "Yadda, yadda" | | ... | | manage_main = DTMLFile("dtml/CMSResource/main", globals()) | manage_edit = DTMLFile("dtml/CMSResource/edit", globals()) | | def __init__(self, resource): | ... So I can call on <a CMS resource>/manage_main and I get this view, etc. The dtml/CMSResource/ directory is of course a directory delivered with the product, it is ``part of'' the product, so to say. Now I'd like to be lazy and try to fetch the views `as needed', instead of declaring them as DTML class methods. Is that possible? What I have in mind is a method |class CMSResource( Traversable.Traversable, ...) | "Yadda, yadda" | | ... | | def render(name, args): | my d = DTMLFile("dtml/CMSResource/" + name) | return <<call d with appropriate arguments>> enriched of course with error checking, caching of things and all that. Whatever I've tried failed because of --as I understand-- acquisition issues (the object returned by calling on d, like d(self, self.REQUEST) is expected to have an aq_parent and the like somewhere in the innards of Zope. My understanding of Zope is not as deep as to have an idea whether what I'm trying is possible (or rather: sensible). Any hints? Thanks, -- tomas
what is supposed to be "lazy" about re-instantiating those DTMLFiles every single time you call that "render" method??? i don't get it. the only "lazy" thing to do would be - as is the usual case - instantiate them once on startup. if you don't have some really good reason why instantiating them once at class level is horribly bad then you should just stick with that. jens On Tuesday, August 13, 2002, at 03:15 , tomas@fabula.de wrote:
Hi,
I'm trying my hands on a Zope product. The different `views' are to be rendered with the help of dtml files, thus the class definition looks something like:
|class CMSResource( Traversable.Traversable, ...) | "Yadda, yadda" | | ... | | manage_main = DTMLFile("dtml/CMSResource/main", globals()) | manage_edit = DTMLFile("dtml/CMSResource/edit", globals()) | | def __init__(self, resource): | ...
So I can call on <a CMS resource>/manage_main and I get this view, etc.
The dtml/CMSResource/ directory is of course a directory delivered with the product, it is ``part of'' the product, so to say.
Now I'd like to be lazy and try to fetch the views `as needed', instead of declaring them as DTML class methods. Is that possible?
What I have in mind is a method
|class CMSResource( Traversable.Traversable, ...) | "Yadda, yadda" | | ... | | def render(name, args): | my d = DTMLFile("dtml/CMSResource/" + name) | return <<call d with appropriate arguments>>
enriched of course with error checking, caching of things and all that.
Whatever I've tried failed because of --as I understand-- acquisition issues (the object returned by calling on d, like d(self, self.REQUEST) is expected to have an aq_parent and the like somewhere in the innards of Zope.
My understanding of Zope is not as deep as to have an idea whether what I'm trying is possible (or rather: sensible).
Any hints?
Thanks, -- tomas
On Wed, Aug 14, 2002 at 12:13:55AM -0400, Jens Vagelpohl wrote:
what is supposed to be "lazy" about re-instantiating those DTMLFiles every single time you call that "render" method??? i don't get it. the only "lazy" thing to do would be - as is the usual case - instantiate them once on startup.
Hmmm, yeah. I was over-concise, lazy, so-to-speak. Of course, in the real thing, I'd cache the instantiation, like so: |class CMSResource( Traversable.Traversable, ...) | "Yadda, yadda" | | ... | | def render(name, args): | if self._templ.has_key(name): | d = self._templ[name] | else: # make new one | d = DTMLFile("dtml/CMSResource/" + name) | self._templ[name] = d | | return <<call d with appropriate arguments>>
if you don't have some really good reason why instantiating them once at class level is horribly bad then you should just stick with that.
But you are right, I'd still get a handful of instances of DTMLFile per CMSResource instance. It's not as bad as you thought, but still, if I've got many CMSResource instances... What I'm looking then is a way to `augment' the class with DTML methods on an `as needed' basis. Still, the difficult part would be <<call d with appropriate arguments>> since it seems to rely heavyly on being called as a class method (the Zope magic doesn't seem to work as it should when I call it as an ordinary function). Thanks a lot for the insight! -- tomas
Hi, just in case someone is interested in how it's turning out. As a reminder: My aim was to use DTML methods on a product without `declaring' them `by hand' in the product class definition. That is, instead of having: | class CMSResource(...): | | ... | manage_workspace = DTMLFile("dtml/CMSResource/workspace", globals()) | manage_main = DTMLFile("dtml/CMSResource/main", globals()) | manage_edit = DTMLFile("dtml/CMSResource/edit", globals()) | ... ad nauseam, I'd have a class method `render(name, ...)' which would lookup a dtml matching `name' (in whatever application-specific way) and instantiate it. (Of course, it might make sense caching this instantiation in some way). My approach now is to dynamically create the DTML class methods, like so: | def component(self, name, ...): | if not self.__class__.__dict__.has_key(name): # FIXME: elegance? | # Add this attribute to the class as a DTML method. | # The Python Gods will surely punish me [REST CENSORED] | self.__class__.__dict__[name] = \ | DTMLFile("dtml/CMSResource/components/" + name, globals()) | return eval("self.%s(self.REQUEST)" % name) # FIXME Anything better than eval? It works -- in a way. The thing finds the right file in the directory given, transforms it into a DTML method and this gets called alright. That is, invoking res.component("foo") will render the DTML in file `dtml/CMSResource/components/foo.dtml' -- but the name space is gone: that's to say the dtml code within this file doesn't `see' the things I'd expect it to (e.g. CMSResource's methods and that). Comments? Ideas? Is there a better way to achieve this? Am I totally crazy? Regards -- tomas
On Thursday 15 Aug 2002 1:37 pm, tomas@fabula.de wrote:
Hi,
just in case someone is interested in how it's turning out.
As a reminder: My aim was to use DTML methods on a product without `declaring' them `by hand' in the product class definition. That
good work
-- but the name space is gone: that's to say the dtml code within this file doesn't `see' the things I'd expect it to (e.g. CMSResource's methods and that).
Comments? Ideas? Is there a better way to achieve this?
http://www.zope.org/Members/htrd/howto/FunctionTemplate
Am I totally crazy?
totally? no. it does get worse than this. I have proof.
On Thu, Aug 15, 2002 at 01:32:25PM +0100, Toby Dickenson wrote:
On Thursday 15 Aug 2002 1:37 pm, tomas@fabula.de wrote:
Hi,
just in case someone is interested in how it's turning out.
[deletia]
good work
That's flattering :-) It doesn't `work' yet... [...]
Thanks, I'm looking into that.
Am I totally crazy?
totally? no. it does get worse than this. I have proof.
You scare me. It already feels... ummm... =:-0 Thanks -- tomas
tomas@fabula.de wrote:
It works -- in a way. The thing finds the right file in the directory given, transforms it into a DTML method and this gets called alright. That is, invoking res.component("foo") will render the DTML in file `dtml/CMSResource/components/foo.dtml' -- but the name space is gone: that's to say the dtml code within this file doesn't `see' the things I'd expect it to (e.g. CMSResource's methods and that).
You can passe the dtml class the namespace: res.component("foo") # this returns you a class which you then can call. res.component("foo")(self, self.REQUEST) That should probably work.
Comments? Ideas? Is there a better way to achieve this? Am I totally crazy?
Hmmm! regards Max M "klaatu verata niktu"
On Thu, Aug 15, 2002 at 02:34:25PM +0200, Max M wrote:
tomas@fabula.de wrote:
[...]
You can passe the dtml class the namespace:
res.component("foo") # this returns you a class which you then can call.
res.component("foo")(self, self.REQUEST)
Thanks, that's what I'm trying right now -- still to no avail. |def component(self, name): ... | return eval("self.%s(self, self.REQUEST)" % name) Thing is... which `self' to stuff in there. Note that I'm working from whithin an object method of res -- that is, self is res. I guess I'll have to pass the ``environment object'' of the DTML (is it called `context' or something?), which I'll then have to pass explicitly to
Comments? Ideas? Is there a better way to achieve this? Am I totally crazy?
Hmmm!
C'mon. Tell me ;-) Thanks -- tomas
tomas@fabula.de wrote:
As a reminder: My aim was to use DTML methods on a product without `declaring' them `by hand' in the product class definition.
This would probably work, but beware of security issues def __getattr__(self, attr): attr = HTMLFile('www/%s' % attr, globals()) return attr(self, self.REQUEST) But I am worried that it might be too slow, as it compiles the dtml for every call. So you could cache it in the object: def __getattr__(self, attr): if not self._dynPages.has_key(attr): self._dynPages[attr] = HTMLFile('www/%s' % attr, globals()) return self._dynPages[attr](self, self.REQUEST) That should be more efficient. regards Max M "klaatu verata niktu"
On Thu, Aug 15, 2002 at 02:57:26PM +0200, Max M wrote:
tomas@fabula.de wrote:
As a reminder: My aim was to use DTML methods on a product without `declaring' them `by hand' in the product class definition.
This would probably work, but beware of security issues
def __getattr__(self, attr): attr = HTMLFile('www/%s' % attr, globals()) return attr(self, self.REQUEST)
I'm a bit wary of redefining __getattr__ in Python. It has bitten me some times now (suddenly some very basic things are gone, like comparison functions and that kind of stuff)
But I am worried that it might be too slow, as it compiles the dtml for every call. So you could cache it in the object:
def __getattr__(self, attr): if not self._dynPages.has_key(attr): self._dynPages[attr] = HTMLFile('www/%s' % attr, globals()) return self._dynPages[attr](self, self.REQUEST)
That should be more efficient.
That's basically what I do in the method `component(...)' I presented in my original posting -- just I cache it as a class method, thus harvesting two advantages: (1) I had the impression that the DTML method expects to be called as class method. So we do it! (2) The class method is instantly available to all class instances (I don't have each DTML method for each instance, but just once for the whole class). Thanks for your comments -- tomas
"klaatu verata niktu"
Just curious: what does that mean?
tomas@fabula.de wrote:
"klaatu verata niktu"
Just curious: what does that mean?
It's just a movie quote. It originates from "The Day The Earth Stood Still" and is used as a magic line in 'Army Of Darkness', where mis-use of it shows what happens if one doesn't concentrate on the job at hand ... regards Max M "klaatu verata <cough>tu"
Am Die, 2002-08-13 um 21.15 schrieb tomas@fabula.de:
Now I'd like to be lazy and try to fetch the views `as needed', instead of declaring them as DTML class methods. Is that possible? It is ;) That's how one usually does it with a "ZPublisher+ZDocumentTemplate" app :)
What I have in mind is a method
|class CMSResource( Traversable.Traversable, ...) | "Yadda, yadda" | | ... | | def render(name, args): | my d = DTMLFile("dtml/CMSResource/" + name) | return <<call d with appropriate arguments>> Well, either this is not your actual code, but it lacks self, OTOH my d seems perlish to me. If you do something like this: return d(...)
you will get back the rendered HTML, not a DTMLFile object. Basically, if you'd post your real method code, we might help you. Andreas
On Thu, Aug 22, 2002 at 09:11:19AM +0200, Andreas Kostyrka wrote:
Am Die, 2002-08-13 um 21.15 schrieb tomas@fabula.de:
Now I'd like to be lazy and try to fetch the views `as needed', instead of declaring them as DTML class methods. Is that possible? It is ;) That's how one usually does it with a "ZPublisher+ZDocumentTemplate" app :)
What I have in mind is a method
|class CMSResource( Traversable.Traversable, ...) | "Yadda, yadda" | | ... | | def render(name, args): | my d = DTMLFile("dtml/CMSResource/" + name) | return <<call d with appropriate arguments>> Well, either this is not your actual code, but it lacks self, OTOH
It does. This was one of my first attempts.
my d seems perlish to me.
It is (*blush*)
If you do something like this: return d(...)
you will get back the rendered HTML, not a DTMLFile object.
Basically, if you'd post your real method code, we might help you.
I snipped down the code to the bare minimum to strip it off unnecessary obfuscation. In the process it seems that some bits of my perl self slipped in... ;-) I'll get back to it soonish. I got distracted with another little fire in the project which is more urgent right now. But lurking a bit on this list already provided me with some ideas. Gimme two or three days... Thanks -- tomas
participants (5)
-
Andreas Kostyrka -
Jens Vagelpohl -
Max M -
Toby Dickenson -
tomas@fabula.de