In general, most sites on the web follow a relatively prescriptive layout.

          

Sometimes the menu is on the right, or below the masthead.  Somethimes one has more than one navigation area. Often, since screens are so wide these days and this impacts on readability, one will have a "Side Matter" area to the right, and perhaps a footer area below the content:

As you can see, we chose to implement a masthead, left side menu and footer.

Using Interfaces to Define a Layout

To recap, Zope Interfaces are a way to declare what attributes and methods a Python object of class might implement.    They do so without making use of inheritance or mixins, but rather allow classes to declare that they implement one or more interfaces through Python decorators or alternatively by using Python class advisors (eg. grok.implements(InterfaceName)).

Interfaces are used extensively by Grok & Zope as a means to defining application components which ultimately get rendered to HTML.  For example, the IBrowserView (specific) and IView (generic) interfaces are used to identify classes which implement a view, where views may define a template.  Classes implementing interfaces which declare attributes that implement zope.schema fields may be rendered to HTML forms.

Adaptors are classes which adapt objects having one interface, and provide objects which implement another.  The Zope architecture provides functions to query adaptors and return objects of a given type.  For example, one might use zope.component.queryAdaptor(my_object, IContentTemplate) to locate a template, where my_object implements an IBrowserView.  The exact kind of template returned is immaterial since we know that IContentTemplate defines the __call__() method, and all templates can be treated in the same way.

What are Marker Interfaces?

This is the part that many people (especially noobs) generally miss about Grok & Zope. I missed it myself when starting with Grok, leading to endless confusion and frustration.

So listen up.

The idea of traversal is to identify a data resource by walking a tree of objects by name, mapping names from the URL.  Then, depending on what is identified, we can either render a default view (index) for that resource, or another named view, or traverse further into the hierarchy.  But what exactly is the type (or class) of the data item we identified by means of traversal? 

Well, interfaces add a whole new amazing aspect to the idea of "duck typing".  Call it "duck typing by proxy":

class ILayout(Interface):
    ''' This is an interface for the main site layout

            All a model has to do to inherit the Layout view, is to say that it
            implements this interface.
    '''

The above is what is called a "Marker" interface.  It is declared as a ZCA Interface, but it does not specify what must be done to implement it.  Consequently, any model component may state that it implements the interface without having to do anything special.

class MyModel(grok.Model):
    ''' A model which must be rendered using the site layout '''
    grok.implements(ILayout)
    ....

We can also use the marker interface in grok.View implementations. If we do this, the view will be rendered for contexts which implement the marker interface.  So, for example, we may define a view called 'index' (the default view) with a context of ILayout, and this view will apply to each and every model which implements ILayout:

class Layout(grok.View):
    grok.context(ILayout)
    grok.name('index')
    ...

Looking at that graphically, if we have for example Model1 and Model2 which both implement an ILayout interface, then an Index view (or Layout View, or whatever) defined with an ILayout context will become available and render for both Model1 and Model2.

In other words, we have a simple way to define a single view which applies to models we define in our application, simply by stating that the model implements (quacks like) a particular interface.  This is great as far as things go, but this is far from the end of our story. 

Page Templates:

Views in Grok are often rendered by means of Zope Page Templates (zpt).  Templates are closer to HTML syntax than python code, and are much easier to develop and maintain than writing all of the HTML as Python strings.  Although Grok supports a variery of templating languages, this site sticks to plain old ZPT.  ZPT and Chameleon (a compiled and optimised ZPT) are similar in many respects in that they are attribute based and valid HTML in their own right.

Consider the following Zope Page Template (layout.pt):

<!doctype html>
<html itemscope="itemscope" itemtype="http://schema.org/WebPage" 
    xmlns="http://www.w3.org/1999/xhtml"
    xml:lang="en"
    xmlns:tal="http://xml.zope.org/namespaces/tal"
    xmlns:i18n="http://xml.zope.org/namespaces/i18n" lang="en">
	<head>
		<meta content="IE=9" http-equiv="X-UA-Compatible" />
		<meta content="text/html; charset=UTF-8" http-equiv="content-type" />
		<meta name="viewport" content="width=device-width user-scalable=no" />
		<base tal:attributes="href python:view.url(context, '')" />
		<title tal:content="context/title | context/description | string:Untitled" />
	</head>
	<body i18n:domain="grok4noobs">
		<div class='MastHead rounded-top' tal:content='structure provider:masthead' />
		<div class='ContentWrapper'>
		   <div class='ContentArea'>
                      <div class='Navigation' tal:content='structure provider:navigation' />
                      <div class='SideBar' tal:content='structure provider:sidebar' />
                      <div class='ContentBox'>
                         <div class='Content' tal:content='structure provider:content' />
                      </div>
    		   </div>
		</div>
            <div class='Footer' tal:content='structure provider:footer' />
	</body>
</html>

 One will notice immediately that the above is in fact an HTML document, but with a few rather strange looking attributes. These all start with the xml namespace tal: and may be explained in the order in which the occur in the above template file:

  • tal:content="context/title | context/description | string:Untitled":
    context is an automatic variable in the template namespace, as are view/viewlet as applicable and request. The meaning of context should be clear from prior discussions, and the content attribute simply tells the template to replace the content of the HTML tag with the value from context.title. If that does not exist, then to use context/description. If that fails too, then use the string "Untitled".
  • tal:content='structure provider:x', where x ∈ [masthead, navigation, sidebar, content, footer].
    This also replaces content within a tag, but this time with what appears to be a "provider".  The structure specifier tells the template language to include the content from the provider verbatim and not to try to escape the HTML prior to inclusion.
  • <base tal:attributes="href python:view.url(context, '')" /> includes a 'base' tag for every page, which allows relative URL's to function in an unambiguous way.  The tal:attributes attribute allows for the definition of attributes for the tag which are based upon data from the application.  In this instance, the view.url() method is called for the current context in order to produce the base URL.
    Without the base tag, the browser will use the current url to determine a relative URL.  The problem is that if the URL contains a trailing slash, the base of the relative link will be different from the case where it does not.  For example,
      relative='pq/rs'
    url='http://ab/cd/' result='http://ab/cd/pq/rs'
    url='http://ab/cd' result='http://ab/pq/rs'

So what then, is a "provider"?  The short answer, is that a provider is a grok.ViewletManager.  and a grok.ViewletManager provides grok.Viewlet's.  There is a longer answer, but to discuss it would serve only to distract.

Viewlet Managers:

Viewlet managers can be seen as pluggable areas of your rendered view.  In our case, we define a viewlet manager for each area that we want to fill differently depending on context.

You will find the following code which defines viewlet managers for each discrete region of our site :

# We specify the renderable areas of the page as viewlet managers.
# These areas will always render for instances of ILayout
class MastHead(grok.ViewletManager):
    grok.context(ILayout)

class Navigation(grok.ViewletManager):
    grok.context(ILayout)

class Content(grok.ViewletManager):
    grok.context(ILayout)

class SideBar(grok.ViewletManager):
    grok.context(ILayout)

class Footer(grok.ViewletManager):
    grok.context(ILayout)

Viewlet managers are called whenever they are asked to render their content.  In the code above, we see that they will render for a context which implements the ILayout interface.  Fair enough, since the layout.pt page template implements the renderer for the Layout view which also renders for instances of ILayout.  This is quite important though, since we could have specified an ancestor interface (eg. Interface) from which ILayout derives, and then the viewlet manager would have had wider scope.

Viewlets:

Of course, viewlet managers render that which they manage.  Namely, viewlets.  The generic masthead viewlet is defined as follows:

class MastHeadViewlet(grok.Viewlet):
    ''' Render layout masthead
    '''
    grok.context(ILayout)
    grok.viewletmanager(MastHead)

As with views, viewlets generally render through means of page templates.  The mastheadviewlet.pt template looks like this:

<div>
    <div style="float:right" tal:content="structure provider:authsection" />
    <div style="float:left">
        <h1>Grok 4 Noobs</h1>
        <h4 class='indent' tal:content='context/title' />
    </div>
    <div style="float:none; clear: both" />
</div>

Of course, everyone should by now know exactly what it produces. (hint: look at the top of this page).

Model-specific content:

To complete the picture, take a look again at our graphic depicting two models, Model 1 and Model 2, both implementing ILayout.  That meant we could have an automatic index view for both models, and a generic masthead that renders different titles depending on which model was identified in the URL.

But what if we wanted to do completely different HTML for say, the content section? Model1 could be an index, and Model2 could be an article, and rendering content for each differs completely!

We can do this by using a specific context: grok.context(Model1) or grok.context(Model2) in a viewlet class will allow one to write two completely different viewlets which differentiate between Model1 and Model2.  In this way, the page becomes adaptive to the kind of data which is the context at the time the page is rendered.

 

Grok 4 Noobs

HTML Re-use and site layout