3.5. Views

Views are a Zope 3 technology for making display components. They are used for certain things in Plone, but because views do not provide all the features users have gotten used to, they must be approached with a little bit of background knowledge.

Page templates promise to separate logic from presentation. Unfortunately, on their own they only deliver half of that promise. How many times have you made a page template that has a very complex python: expression and thought "I should move this out of the page template"? The options until Zope 3 have been:

  • Make ever more complex python: expressions in the page template and look the other way when people ask who the hell put that in there. Unfortunately, such expressions are difficult to maintain and debug, nearly impossible to test, and expensive to execute.
  • Make a new Script (Python) in a skins folder such as plone_scripts. Unfortunately, skin scripts are hard to debug, hard to test, quite slow to execute and litter a global namespace.
  • Put the code in a content type class that is the context of the script. This is quite common in Archetypes, for example. The problem is that this bloats the interface to that class with quite display-specific code, making the type and (particularly) the page template more difficult to re-use.
  • Put the code in the plone_utils tool (PloneTool.py). This has made PloneTool an incredibly bloated set of losely related functions that may or may not be useful to more than one or two templates.

In Zope 3, this is solved with a view. A view is basically just a multi-adapter that is looked up on the context object (usually a content object) and the request (so that the view can be different for HTTP requests that for FTP requests, say). In pure Zope 3, views are used to represent objects to XML-RPC or FTP and typically comprise a Python class implementing a particular interface and a page template. Views are registered in ZCML under a given name and referred to by @@name (the @@ looks like a pair of eyes peering out, if it helps you remember the symbol).

In Plone 2.5, we used to use a custom base class in Products.CMFPlone.utils.BrowserView and a related utility method in Products.CMFPlone.utils called context() to deal with acquisition problems in views. However, this is now frowned-upon, because it introduced a hard dependency on CMFPlone and slightly deviates from the agreed IBrowserView interface.

Instead, views tend to be used like this in Plone 3:

 from Acquisition import aq_inner
 from Products.Five.browser import BrowserView

 class MyView(BrowserView):
     """My new view
     """

     def some_function(self):
         context = aq_inner(self.context)
         ...

Notice the use aq_inner(). This ensures that the acqusition chain of the context does not include the view as a parent. There are numerous examples of browser views in the plone.app.* packages.

View tips and tricks

  • View can (and should) be unit tested! They are just simple python classes, nothing else.
  • Views are transient - they are never stored in the ZODB, and never acquisition-wrapped.
  • Views are created fresh for each request cycle. Therefore, they cannot cache values directly between requests, nor can they rely on any form of persistence.
  • Sometimes it's necessary to call a view method more than once. If that method does some expensive calculation, you should cache the results of the operation. To make this easier, a cache decorator exists in plone.memoize. Using it is pretty simple:
      from plone.memoize.instance import memoize
    
      class MyView(BrowserView):
        ...
    
        @memoize
        def someExpensiveOperation(self):
            x = 10
            for x in range(10000):
                x = x ** x
            return x