Zope (and Grok) are not really magical, although reading some of the code may leave the unintiated wondering how the framework manages to do things so sweetly and with so little effort.

Here follows a concise explanation of most of the core concepts surrounding a Zope/Grok application architecture.  As understanding creeps in, some of the magic will evaporate into thin air!

Basic concepts contained in Zope 3 (Bluebream) and Grok:
  1. An application server is a collection of applications, available to the application server by an admin registering an application.
  2. A site is an instance of an application and has a URL entry point and name
  3. A site is like a directory structure; imagine pages or HTML snippets were documents in the file system, each with their own path (or URL).  Route configuration may therefore be largely automated.
  4. The Zope publisher converts HTTP requests into request objects, calls Python callables (views) which return response objects, and then converts the response object into HTTP responses.



  5. A URL is interpreted by the publisher to provide both a data object, and a view object.  A request object is provided by the WSGI layer as shown in the diagram above.


    The above simply says that "app" is the name of a Data item which is an instance of "Model", and that "appview" is the name of a view on the model.  We can look up a View object using the Data object and a Request object (provided by the WSGI layer) and create an HTTP Response by calling the View.
    1. Data objects are run time instances of a defined Python data class (a model). 
    2. An application name ("app" in the above URL) is specified when a Grok application is installed by the Zope Management Interface (ZMI).  A Grok application is simply a special type of Python data object (a kind of model), deriving from grok.Application.
    3. The default view name, unless specified in the URL,  is 'index'.
    4. Data objects may be containers for other data objects, forming a hierarchy.
      1. Data objects are identified through the traversal process (i.e. reading the URL) by name.
      2. A data object that results from interpreting the URL is a location.  A location has a name, and a parent.
      3. A Model class name does not define the name of a data object!  For example, if the class name of a model is Book, then the data object name is "Moby Dick" or "Freckles", not "Book".
    5. Data objects may be created on demand (transient instances of a model) or persisted in a database.
    6. ZODB based URL's are interpreted by traversing the ZODB hierarchial structure, and identify persistent data objects.
      1. The traject and megrok.traject packages provide for route based dispatch where desired.
      2. Whether traversal or routing is used for URL dispatch, the URL always defines a model.
      3. This is different from most other web frameworks, where URL's typically identify only a view which must instantiate a model using arguments where necessary.
    7. View objects are python callables (functions or objects) which return an HTML response as text, or something that can be turned into an HTTP response.  Views are instantiated using both a data object and a request object.
  6. The Zope Component Architecture (ZCA) provides the hard wiring for the framework.  The ZCA lets a developer write pluggable components which can be easily updated or replaced in part without breaking the whole.



  7. Generic types (interfaces) can be associated with models. 
    1. An interface defines a model's methods and attributes, but not their implementation.
    2. A model may implement zero, one or more interfaces.
    3. A component is a Python object (such as a model) which implements an Interface.  So for example, "Moby Dick" in the above diagram is a component which implements an IBook interface.
    4. Knowing the type for a data object enables some intelligent behaviour.
      1. Automatic HTML forms
      2. Validation of attribute (or field) values
      3. Converting one type into another type using an adapter.
    5. Interfaces may inherit from other interfaces to form a type hierarchy (or logical hierarchy).
      1. For example, an integer and a float are both a type of number.
      2. A model inheritence (or physical) hierarchy is not needed to implement generic behaviour, as is the case for Python ABC's.  So dependency hell is neatly averted.
  8. Singletons may be registered as persistent LocalUtility or non-persistent GlobalUtility objects.  Typical uses for such objects would be to store user identities in a persistent database, or to contain reference data which should be initialised only once at startup.  These object types are registered with and located via the ZCA.
  9. Adapters are converters from one registered type into another.  For example, we may need to find a search interface for a book.  At another time, we might want a search interface for an address list.  The search interface remains the same, but the context might change.


    For example:
    class BookSearch(grok.Adapter):
      grok.context(IBook)
      grok.implements(IIndexSearch)
    
      def __new__(self, book):
        return BookIndex(book)
    
    1. The ZCA can look up and return a registered callable based on what is being adapted, what it's being adapted to, and a name.  This happens very efficiently.
      eg. book_index = IIndexSearch(mybook)
      or  book_index = queryAdapter(mybook, interface=IIndexSearch, name=u'')
      ...etc
    2. This provides for numerous simple or advanced patterns, for example,
      1. turn a banana object, which is a type of fruit, into an RGB colour. color = IRGB(banana)
      2. take an svg XML text data element, and turn it into a rendered PNG. res = IPNGResult(mysvg)
      3. take the same XML text element, and turn it into a JPEG. res = IJPGResult(mysvg)
      4. convert a person object and a request object into HTML representing an edit form called 'edit'.
        form = getMultiAdapter([person, request], name='edit')
        html = form()
      5. convert a request object into an authenticated session object...Adapters convert one thing into another thing.  They are Python classes or callables registered with the ZCA.
        session = ISession(request)

  10. Views are defined against either models, or interfaces.


    For Example:
    class BookIndexView(grok.View):
      grok.context(IBook)
      grok.name('index')
    
      def render(self):
         return "<p>A book index view</p>"
    

    1. A view against an interface is available to all models which implement that interface. So a cookbook and a story book (specialisations of IBook) can both share a visual search interface.
    2. Traversal of a URL provides a data object (called a context).
    3. Views are registered adapters which convert a data type and a request into an HTTP response.
    4. Views may be rendered using a template (zpt, chameleon, genshi, jinja2 etc), or directly in Python code.
    5. The main difference from routing based frameworks, is that with traversal the view already has a model instance (the context) at the time it is called.
  11. Basic persistence mechanism for application registration and configuration is provided via ZODB, and this use may be used for general application data persistence as well.  However, nothing prevents the use of other methods of persistence for your application (sqlite/postgres/mysql/etc).
  12. Zope toolkit (ZTK) provides a set of modular extensions for easy inclusion in your app.  For example:
    1. zope.authentication: Add authentication for users, and access control to your views.
    2. zope.catalog:  Generate ZODB indexes to enable search in your ZODB based app.
    3. zope.copypasteremove: CRUD on your ZODB stored items
    4. zope.formlib: Automatic zope forms widgets
    5. zope.schema:  Some field definitions for translation in zope.formlib
    6. etc.  The library is quite extensive.  A good place to read about the ZTK, is the Zope Toolkit documentation. Another good place is the Grok documentation.
    7. Many of the modules are independent, and may be used outside of Zope 3 or Grok based applications.
Differences between Grok and Zope 3:
  1. Both Zope 3 and Grok make use of ZCML (Zope Configuration Markup Language) to configure the framework.  However, Grok makes use of it to a much lesser extent. 
    1. While some may not have issue maintaining configuration in XML files apart from the Python code, many if not most developers would view this negatively.  As someone recently remarked, that's not separation of concerns, that's separation of technologies!
    2. Grok uses a library called martian to implement Grok directives.  These directives may be used to configure the framework at load time.  This greatly simplifies the task of maintaining a Zope based site.
    3. What kind of configuration?  In Zope, anything that needs to be registered with the ZCA is normally configured using ZCML.  This includes views, adapters global and local utilities, notification messages, access control items and relation to views and so forth.  Grok uses directives for all these cases.
  2. Grok wraps some of the Zope 3 modules to present a friendier interface.  This does not restrict in any way your access to Zope modules.
  3. Some extension modules to Grok make it exceedingly simple to implement things like login pages, relational database access via SQLAlchemy, etc.
  4. The Zope Management Interface (ZMI) for Zope 3 is currently friendlier and more functional than that of Grok.

Grok 4 Noobs

Grok & Zope in a Nutshell