[Zope3-dev] Brainstorming about browser pages

Jeff Shell eucci.group at gmail.com
Sat Feb 18 14:04:30 EST 2006


On 2/18/06, Roger Ineichen <dev at projekt01.ch> wrote:
> Hi Steve
> >
> > The advantage of all this is that you need to look in just
> > one place to
> > understand a view class.  You don't need to look in both the ZCML and
> > the Python code, just the Python code.  The ZCML becomes simpler, and
> > more focused on glueing pieces of Python code together, and less about
> > what is to be displayed at what URLs.
>
> Let my say somthing about that.
>
> I like it and I think it's a good way for simplifie view registration
> for a well defined project but not a fremawork where has to offer
> a more open API for views.

> Why not useable in frameworks;
> because this means you have to use PageTemplates for such a API.
> But Im OK with this as long we don't propagate this as a reusable
> concept for the zope3 core or 3rd party applications.
>
> I like it more to see real Pyhton view API's where can be used
> with different templating systems in the future.

I don't understand what you mean by this. At its most basic, a 'view'
in Zope 3 is just a multi adapter. It takes a context object (any
object) and a browser request and provides... Well, whatever it wants
to provide. Not all views are named. Not all views have page
templates. See Absolute URL, for example.

Beyond that, what kind of "open API" do you need? You can have as open
of an API as you want.

I have 3 way multi adapters (context, request, parent view - but not
content providers) that exist purely for formatting. I kept having to
display the same core set of information for an Article - format a
date, turn a user id into a printable name, list tags. There were some
situations where one of those things needed to be formatted
differently: in a search results view, the matching 'tags' needed to
be highlighted. In other views, the 'tags' needed to be links to find
other Articles with that tag. So I made a browser view type object
just for this. Another view (the view listing the articles) is what
calls it and renders the formatted bits that it wants. The adapter is
queried like "getMultiAdapter((article, self.request, self),
IFormattedArticleRecord)", with ``self`` referring to a browser view.

I break things out like this a lot. I also don't use page templates
very much in these smaller views, but instead use an internal HTML
generation tool with ideas liberally borrowed from Nevow's stan, and
an html "helper" class to provide a common API for useful common
things (helper is an adapter for a request).

    html = cmsapi.htmlHelperFor(self.request)
    T = fdlib.tags.builder(indent=2, separator='\n')
    outer = T.ul(class_='articles')
    for article in self.context.values():
        formatted = zapi.getMultiAdapter(
            (article, self.request, self), IFormattedArticleRecord)

        outer << T.li(id='art_%s' % zapi.getName(article))[
            html.linkTextTo(formatted.title, html.url(article)),
            html.linkTextTo(
                '[Delete]', html.viewURL(article, 'delete'), post=True,
                confirm="Delete article %s?" % formatted.title, class_='button'
            ),
            T.br,
            T.strong['Modified: '], formatted.modified,
        ]

    return unicode(outer)

Page template free! And sure to make others cringe :).

If you need an API, make interfaces and make views that provide those
interfaces. But often, a view is really about output for a particular
display, and that should really be its main job.

And if you find yourself formatting or looping or walking a certain
set of data the same or similar way much of the time, extract that
into a separate class and use it directly or provide it as an adapter
or utility. Follow the law of threes: if you do something once, you
don't need an API for it. If you do it twice, copy and paste and sigh
but you don't need an API for it. Do it three times, and you should
probably extract it out into a helper.

> btw, I think this isn't really possible since it exist two
> base templating pattern (push and pull). But a python view
> class can always offer a API with the right set of methods and
> permissions. (MVC)

Yes it can. It can now. Although 'right set of methods and
permissions' is a very vague concept. If you design your view properly
(in my opinion), it should be a small enough unit of work that the
whole view could be protected by just one permission. This comes down
to where you place responsibilities - does an object listing view also
take on responsibilities for cutting, pasting, moving, deleting,
renaming, etc? I used to say yes, but now I vehemently say 'no!' Move
those responsibilities off into supporting smaller views, I say! That
doesn't mean you can't provide a helper API that groups those
responsibilities into a useful tool, but use that API internally.

I don't think it should be the primary responsibility of a view object
to provide an API. Its primarily responsibility should be rendering
content for output. But again, your view can provide an interface and
you can program against that. Just make the decision - "am I making
this object to render output?" or "am I making this object as a helper
for common operations my views need?" and design appropriately.

Core API's and concepts should be simple. Look at zope.contentprovider
- 'update' and 'render'. That's all one really needs. You don't need a
big API supporting, well, I don't know what you'd want to support.
That's the responsibility of systems that build on Zope 3, whether its
a big application like launchpad or an internal CMS framework you use
to support your own customers. Those systems will have their own
needs. What's helpful for me may not be helpful for you. What works
for Plone may not work for an e-commerce solution. I like small
cooperating components, whether they're views or helpers or models or
utilities. Others prefer bigger classes that take on more
responsibilities on their own.

> If we think about MVC then I would say that such a pattern
> isn't the right way to mix it with PageTemplate implementation.
> But that's another part.

It sounds like you want the C more than the V. Personally, I've always
had an issue with MVC, which is that in so many cases, Views and
Controllers really have a tight relationship and for many uses an
object could be both. In Zope 3, you can use ``template =
ViewPageTemplateFile('foo.pt')``, but you can also just do what I do
above and use another HTML generation system. Or use meld3 or any
other offering available. This isn't a Rails type environment where
the 'views' are templates of a particular type that are in a special
directory. You have complete control. I like having my 'views' be
'controllers', and be full intelligent Python objects. A template is
just there to support rendering if I need it. A template is too dumb
to be a view object in my opinion, and I would hate to see
restrictions along those lines.

I think my style of development these days could be described as
MVWBoH (model, view, and a whole bunch of helpers).

> But I agree with you Steve, I think its a good concept if you
> use it in project which uses PageTemplates and doesn't need to
> offer a generic API.

I think the ``update / render`` pair concept (as an elaborated concept
of __call__ on view classes) is great. Steve's concept is similar. It
has nothing to do with page templates, except as a minor
implementation detail. I think __call__ is about the best generic API
we can use right now. On top of that, the 'update/render' API used in
zope.formlib, zope.contentprovider, and zope.viewlet is the best
extension. Why? Because you can look up smaller 'views' within a
larger view and get them ready for display, but not run them through
the rendering stage yet. You can prepare them without having to look
up a richer API, or wonder whether __init__ has set things up yet, or
whether some internals won't really be prepared until the rendering
stage. It's so beautifully simple, and I (personally) have gotten
terrific mileage out of following that pattern for many of our own
views.

--
Jeff Shell


More information about the Zope3-dev mailing list