Any class deriving from grok.Application may be added via the Zope Management Interface (ZMI) to the grok application server.  You may even add multiple instances  of the same application. For instance, this application is a wiki (sort of). If we wanted to run multiple wiki's with different url's we could simply create as many instances of our application as we wanted.

ZMI & the Site Manager

The core of the Grok application server is the Zope Management Interface.  Persistence for the ZMI is provided by a single ZODB database.

Each application registered with the ZMI and served by the Zope application server, is represented by a site manager.  Site managers are classes which implement the ISite interface.  IApplication is a subclass of ISite, and the grok.Application class implements the IApplication interface.  The Zope Management Interface recognises application candidates for registration because they implement IApplication and are registered with the Zope Component Architecture registry.

Each site manager instance is the root of a persistent object store (ZODB) for a Grok application. The site manager provides a ZCA registry for local utilities and local adapters.

A basic application

Revisiting the simple hello world application we presented earlier, we can see that it consists of a model, and a view.  An instance of the model may be stored as an object in the Zope application server, and given a name.  That name then becomes the base URL to access the application from a browser.  The default view name is 'index', so simply navigating to the URL will call the render() method of the view, and produce the web page.

import grok

class App(grok.Model, grok.Application):
   ''' This is an application called "app" '''

class Index(grok.View):
   ''' This is the default view for "app" '''

   def render(self, who='Anonymous'):
      return """<p>Hello {}</p>""".format(who)

In the class App, the grok.Model (or grok.Container if you like) implements the IPersistent interface, allowing the object instance to be persistently stored in ZODB, and the grok.Application implements IApplication and ISite, effectively becoming a Grok installable application and site manager for the application.

Traversal and Views

When the Zope publisher finds an object by traversing the URL path and mapping it to a hierarchy defined either in code or by the ZODB, the publisher ends up with either an object, or an object and a view name.  For example, the URL http://mysite/myapp/bob might end up with an application 'myapp' and view name 'bob', or it may end up with an object 'bob' if it is defined.  Where there might be confusion, eg both an object in myapp named 'bob' as well as a view for myapp named 'bob', the publisher will return the object 'bob' unlesss the url is specified as http://mysite/myapp/@@bob, in which case the publisher explicitly looks for a view 'bob'.

If through traversal, the publisher cannot identify a view name, the view name defaults to 'index'.  Thus, if http://mysite/myapp/bob identifies an object 'bob', we assume the view name to be 'index'.

To locate and instantiate a view object, the publisher tries to look up a view named 'index' for object bob.  If found, the publisher calls the view, and then if IResult is not already provided, tries to adapt the response to an IResult. The final response is then returned.

Views are implemented as adapters, which adapt an (object and an IRequest) to an IView.  IBrowserView, IFTPView, IHTTPView and IXMLRPCView are all specialisations of IView. Normal Grok views implement the IBrowserView interface.

View Templates

If we were to include all of our HTML in Python render() methods as shown above, things would quickly become unmanageable.  For one thing, syntax highlighting of simple strings becomes impossible in editors, and there is no support for syntax checking or error reporting.

So a better way is to define your code in a template.  While Grok supports various template types, one is generally better off using Chameleon or Zope Page Templates (ZPT) since those formats are effectively just HTML/XHTML with attribute markup.

<!doctype html>
<html>
  <head></head>
  <body>
     <p>Hello <span tal:content="request/who|string: World"></span></p>
  </body>
</html>

For obvious reasons, one cannot have both a render() method and a template defined for the same view.  The above ZPT template should be self explanitory.  It produces "Hello World", or if 'who' is defined in the URL as "Bob", produces "Hello Bob".

The TAL namespace contains numerous definitions, allowing all of the constructs one might find useful in producing HTML.  There is a ZPT language reference provided here.  Chameleon is very similar, but differs in some syntactical aspects and is compiled rather than interpreted at runtime for speed.

Variables such as request (the view request object), context (the model instance) and user (the authenticated user object) are automatically available to view templates and views.  Other automatic view variables include view (the view object itself) static (the static resources folder view) or dynamic variables, for example a loop state container.  User defined variables may be created using tal:define="varname1 value1; varname2 value2[; ...]" statements, or in Python code from within the view class itself by either defining view attributes and referring to them as view/<attribname> in the template, or by overriding the namespace() method in the view class.  The namespace() method returns a dict which exports names and values to the template.

Initialising a view is done by overriding the update() method for views.  As with the render() method, any arguments passed to the URL will appear as named arguments to the view.update() method, which is always called prior to render().

Zope Schemas

zope.schema introduces several interesting definitions, such as zope.schema.TextLine, zope.schema.Choice, zope.schema.Bool, zope.schema.List, etc.  These are all classes which derive from zope.schema.Field, and the intention of a field is to provide a data type which contains descriptive information and constraints, and can be checked for validity against those constraints.  For example, a zope.schema.Int may be restricted to range min=1, max=10

Rather than the field object itself containing the data for the field, a field may be bound to another objects attribute by name, so an ordinary attribute which stores a text string may be mapped to a schema.TextLine having a max length of 20, or a schema.Password field for example.

To demonstrate, consider the following:

from zope import schema
from zope.component import Interface

class ILoginForm(Interface):
    ''' Our login form implements login and password schema fields.
    '''
    login = schema.BytesLine(title=u'Username', required=True)
    password = schema.Password(title=u'Password', required=True)

A schema.BytesLine restricts the content to just bytes (or in other words ASCII text if you will).  We will get back to schema.password momentarily.  Given the above interface, we could define a model to match:

class Account(grok.Model):
    grok.implements(ILoginForm)
    login = ""
    password = u""

    def __init__(login="", password=u""):
        self.login, self.password = login, password

Now, since we specify that the ILoginForm is implemented by Account, the login and password fields are described by the schema, and we can use zope.schema.getSchemaValidationErrors(ILoginForm, ob) to check compliance.  Automatic compliance checks can be added by defining attributes as

from zope.schema.fieldproperty import FieldProperty
login = FieldProperty(ILoginForm['login'])

 This will check the login attribute every time it is assigned a new value, and raise a ValidationError if it cannot be validated.

Views on Fields

Just as the zope publisher looks up a view for an object and then calls it to produce HTML, we can do the same for any object of our own, and that includes schema fields.  Thus, a zope.schema.TextLine can have a view which returns a snippet of HTML which renders an HTML input element wrapped in some formatting fluff. Such views, as defined against schema fields are called widgets, and form the basis for automatic form generation.

Although zope.schema.Password is derived from zope.schema.TextLine, it's default widget renders as an HTML password input rather than as a textline input.

A grok.AddForm, for example, is a view which collects all the schema elements and in turn looks up widget views for each field, and then renders them one after the other inside a table embedded in an HTML form element.  When processing the form request, it maps incoming form data to their corresponding fields and updates the model attributes corresponding to the schema field definitions.

So, we can have an AddForm, an EditForm and a DeleteForm, which require little or no work to produce directly from Python schema-mapped classes.  grok.formlib, which wraps zope.formlib, is one library which provides widgets.  It is reasonably trivial to extend existing widgets or produce one's own.  Deriving a new field type and then defining a new widget for your new field is a good way to support multiple input methods for the same basic kind of data field.  For example, one might render radio boxes either horizontally or vertically by simply switching out a CSS class and doing some styling.

So, for example were we to define:

class Login(grok.AddForm):
    grok.context(Interface)
    grok.require('zope.Public')

    @grok.action('login')
    def handle_login(self, **data):
        ''' Use the data to log in
        '''

the result might look a little like this:

Grok 4 Noobs

How to make applications with Grok