[Zope3-dev] Brainstorming about browser pages
steve at canonical.com
Sat Feb 18 04:03:14 EST 2006
I'd like to chip in with what we're doing on Launchpad, and where that's
> This is another infamously long post of mine. I'll summarize my
> thoughts on the brainstorming here, with details below::
> Summary / Quick Thoughts
> I'm not fond of <browser:page>. I can't wait to revisit and remove a
> few uses of <browser:pages> that I'm still supporting because it's
> proven to be very unwieldy. I find <browser:view> to be very useful in
> its current form. I would like to see something that made exposing
> view attributes as URL traversable elements as easy (or easier) than
> formlib actions. I'd like to see, perhaps, a 'pagelib' that does this.
> Or maybe just a little bit more work in 'zope.formlib.page' to promote
> the 'update/render' style (which all of the page.Page based classes in
> formlib.form use anyways), with some equally usable base classes that
> made writing a template-only view a two line affair, while allowing
> that view to grow in capability by adding / providing more methods::
> class PageView(zope.formlib.page.Page):
> def update(self):
> Subclasses may use this to respond to requests and get items
> ready for rendering
> def render(self):
> raise NotImplementedError("Subclass should provide render")
> def __call__(self):
> return self.render()
In Launchpad, we have a very similar LaunchpadView base class. It has
an initialize() method where you have an update(), and it had render()
and call() similarly to what you have.
This pattern works well. Also, initialize() is often used to set up
attributes in the class that the class' page templates will use repeatedly.
If you're going to be at pycon, I'd love to talk about this.
> class TemplatedPageView(PageView):
> template = ViewPageTemplateFile('empty.pt')
> def render(self):
> return self.template()
We often have more than one template per class in Launchpad.
In fact, the general pattern is to have one class per area of the data
model, because view-level methods and computed attributes are very often
shared, and because it makes communicating among the 15 or so people on
the development team easier. We can just talk about "in the browser
code for distribution" and developers know we're talking about
browser/distribution.py and probably about the DistributionView class.
We'll be moving soon to having minimal ZCML configuration for pages,
just pointing at the module and class to use for a set pages. Within
the class there will be methods decorated with @page('name'), and
attributes for page templates. So, a typical view class in Launchpad
will look like:
# here, set up data before the page is rendered,
# do redirections and avoid the page being rendered,
# and process form POSTs.
foo_page = page('foo.html', LaunchpadTemplate('foo.pt'))
bar_template = LaunchpadTemplate('bar.bt')
# do stuff, maybe using self.bar_template
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.
There is a subtle bug in how Zope's views are set up (related to
traversal magic in the View directive) that prevents having multiple
entry-points into the same class, like this. I'd like to fix these
bugs... another thing for pycon.
LaunchpadView has some other code in it too, for making a 'self.user'
attribute lazily available. We adapt the principal to a Person object
(one of our content types), and all application code using Person
objects rather than Principal objects. Principals are kept just in the
web publication and security code.
More information about the Zope3-dev