Accessing browser view attributes

in one of our projects we have a browser view that returns a value we use on a Diazo theme.

class IFoo(Interface):
    def bar():
        """Return bar."""

@implementer(IFoo)
class Foo(BrowserView)
    def bar(self):
        return u'bar'
    def __cal__(self):
        return self.bar()

we now need to add another feature and I was wondering if it is possible, instead of creating a new browser view, to be able to traverse to the functions on it to get their results.

I was testing with the allowed_interface of the view, but when I try to access the function (using /@@foo/bar) I get a 404 status code as return:

<browser:page
    name="foo"
    for="*"
    permission="zope.Public"
    class=".Foo"
    allowed_interface=".IFoo"
    />

is there a way to achieve this?

Your approach appears a bit weird since /@foo would resolve to the call() method of the browser view because you did not specify a template or attribute attribute in ZCML. So /@foo/bar would lookup the bar attribute of the call() method?! So this goes into the direction of function attributes but this is not what you are looking for.

Off-topic: function attributes in Python?! Any real-world use cases for them?

-aj

sorry, seems I didn't explain myself well: currently we have this on our Diazo theme:

<replace css:content=".foo" css:theme=".foo" href="/@@foo" />

so what happens now is I call the browser view and I get u'bar' as a result and I use that on the themed site.

so, right, I'm using the view __call__ method now to achieve this.

the use case is that I have to add a new feature that requires something like that again, and I was wondering if there's a different way to solve this besides the obvious creation of another helper view.

so my initial though was: "what if I can access the result of a function on the view directly", that way I can add any number of functions and use them.

I know this works in other places (like page templates), but I don't know why it doesn't work when you traverse directly to the function in the browser.

should I just add a new browser view, then?

I think everything you want can be achieved implementing properly a publishTraverse method, like the resolveuid view is doing:

https://github.com/plone/plone.outputfilters/blob/387344adf31ab5ceda49ad289c0bdb867d6ada26/plone/outputfilters/browser/resolveuid.py#L47

Check also:

1 Like

yes, this seems what I was missing; thanks!

1 Like

Slightly off topic (again... ) but it would be possible to call the browser view like

/@@foo?iwant=bar

/@@foo?iwant=shop

1 Like

I was not able to use the __getitem__ solution, but IPublishTraverse worked like charm:

@implementer(IPublishTraverse)
class Foo(BrowserView):
    def publishTraverse(self, request, name):
        if name not in ('bar',):
            raise BadRequest
        self.name = name
        return self
    def bar(self):
        return u'bar'
    def __call__(self):
        return getattr(self, self.name)()

accessing with /@@foo/bar now returns u'bar'.

1 Like