The grok module defines a set of base classes for creating new components of different types, that provide basic Zope 3 functionality in a convenient way. Grok applications are built by subclassing these components.
Components in Grok and Zope 3 are any plain Python object, that declare they provide one or more Interface(s). The inclusion of these Grok base classes in your own class inheritance automatically handles the component registration with the Zope Component Architecture. This process of introspecting your Grok code during initialization and wiring together components based on common conventions that you follow in the structure of your source code is called “grokking”.
Applications are top-level objects. They are typically used to hold global configuration and behaviour for an application instance, as well as holding data objects such as grok.Container and grok.Model object instances.
When a new Grok application is installed using the Grok Admin interface, it creates an instance of the grok.Application class and saves it in the root of the main ZODB database.
Model objects provide persistence and containment. Model in Grok refers to an applications data model - that is data which is persistently saved to disk, by default in the Zope Object Dataabse (ZODB).
Base class to define a content or model object.
The parent in the location hierarchy.
If you recursively follow the __parent__ attribute, you will always reach a reference to a top-level grok.Application object instance.
The name within the parent.
Traverse the parent with this name to get the object.
Name in Grok means “human-readable identifier” as the __name__ attribute is used to reference the object within an URL.
Objects in a container are manipulated using the same syntax as you would with a standard Python Dictionary object. The container implements the zope.container.interfaces.IContainer interface using a BTree, providing reasonable performance for large collections of objects.
Base class to define a container object.
The parent in the location hierarchy.
If you recursively follow the __parent__ attribute, you will always reach a reference to a top-level grok.Application object instance.
The name within the parent.
Traverse the parent with this name to get the object.
Name in Grok means “human-readable identifier” as the __name__ attribute is used to reference the object within an URL.
Return an iterator over the key-value pairs in the container.
If None is passed as key, this method behaves as if no argument were passed.
If key is in the container, the first item provided by the iterator will correspond to that key. Otherwise, the first item will be for the key that would come next if key were in the container.
Return an iterator over the keys in the container.
If None is passed as key, this method behaves as if no argument were passed.
If key is in the container, the first key provided by the iterator will be that key. Otherwise, the first key will be the one that would come next if key were in the container.
Return an iterator over the values in the container.
If None is passed as key, this method behaves as if no argument were passed.
If key is in the container, the first value provided by the iterator will correspond to that key. Otherwise, the first value will be for the key that would come next if key were in the container.
Get a value for a key
A KeyError is raised if there is no value for the key.
Get a value for a key
The default is returned if there is no value for the key.
Add the given object to the container under the given name.
Raises a TypeError if the key is not a unicode or ascii string. Raises a ValueError if key is empty.
The container might choose to add a different object than the one passed to this method.
If the object doesn’t implement IContained, then one of two things must be done:
The object’s __parent__ and __name__ attributes are set to the container and the given name.
If the old parent was None, then an IObjectAddedEvent is generated, otherwise, an IObjectMovedEvent is generated. An IContainerModifiedEvent is generated for the container.
If the object replaces another object, then the old object is deleted before the new object is added, unless the container vetos the replacement by raising an exception.
If the object’s __parent__ and __name__ were already set to the container and the name, then no events are generated and no hooks. This allows advanced clients to take over event generation.
Delete the named object from the container.
Raises a KeyError if the object is not found.
If the deleted object’s __parent__ and __name__ match the container and given name, then an IObjectRemovedEvent is generated and the attributes are set to None. If the object can be adapted to IObjectMovedEvent, then the adapter’s moveNotify method is called with the event.
Unless the object’s __parent__ and __name__ attributes were initially None, generate an IContainerModifiedEvent for the container.
If the object’s __parent__ and __name__ were already set to None, then no events are generated. This allows advanced clients to take over event generation.
Example 1: Perform Create, Read, Update and Delete (CRUD) on a container
# define a container and a model and then create them
class BoneBag(grok.Container): pass
class Bone(grok.Model): pass
bag = BoneBag()
skull = Bone()
# ... your classes are then "grokked" by Grok ...
# store an object in a container
bag['bone1'] = skull
# test for containment
bag.has_key('bone1')
# retrieve an object from a container
first_bone = bag['bone1']
# iterate through objects in a container with .values()
# you can also use .keys() and .items()
for bone in bag.values():
bone.marks = 'teeth'
# delete objects using the del keyword
del bag['bone1']
OrderedContainers act just like Containers, but also support the ability to maintain order on the items within the container. This implementation maintains a persistent list of keys on a private attribute, so it’s important to note that OrderedContainers will have poorer performance than a normal Container.
Base class for an OrderedContainer. OrderedContainer inherits from Container and supports the same interface.
Revise the order of keys, replacing the current ordering.
order is a list or a tuple containing the set of existing keys in the new order. order must contain len(keys()) items and cannot contain duplicate keys.
Raises TypeError if order is not a tuple or a list.
Raises ValueError if order contains an invalid set of keys.
Indexes are local utilities for holding a set of indexes alongside a grok.Site or grok.Application. An index is a data structure that provides a way of quickly finding certain types of objects, i.e. it provides catalog functionality for attributes/contents of stored objects.
The site or application that the indexes are intended for should be named with the grok.site() directive, and the kind of object to index should be named with a grok.context() directive.
Inside their class, the developer should specify one or more grok.index.Field, grok.index.Text, or grok.index.Set instances naming object attributes that should be indexed (and therefore searchable). See grok.index module for more information on field types.
Note
Indexes are persistent: they are stored in the Zope database alongside the site or application that they index. They are created when the site or application is first created (and made persistent), and so an already-created site will not change just because the definition of one of its grok.Indexes changes; it will either have to be deleted and re-created, or some other operation performed to bring its indexes up to date.
Base class for index collections in a Grok application.
Directives:
Required. Identifies the type of objects that should be catalogued. The context type can also be set module-wide.
See also
Required. Identifies the type of site or application in which objects should be catalogued.
See also
Example 1: Index the Mammoths in a Herd
Imagine you have a herd of mammoths, and you wish to quickly find a mammoth based on their last name. First we will create a simple Grok application that defines a Herd and some Mammoths:
import grok
import grok.index
import zope.interface
import zope.schema
class Herd(grok.Container, grok.Application):
pass
class IMammoth(zope.interface.Interface):
full_name = zope.schema.TextLine(title=u'Full Name')
class MammothIndexes(grok.Indexes):
grok.site(Herd)
grok.context(IMammoth)
full_name = grok.index.Text()
class Mammoth(grok.Model):
grok.implements(IMammoth)
def __init__(self, full_name):
self.full_name = full_name
We can now create a Herd application, add some Mammoths to the Herd, and query for those Mammoths by their last name.
Imagine root is our ZODB root as provided for instance by a debugger:
>>> herd = Herd()
>>> root['herd'] = herd # <-- the indexes are created here
We manually set the ‘site’ to search. This happens automatically when we do for instance regular browser requests:
>>> from zope.site.hooks import setSite
>>> setSite(herd)
Populate the herd:
>>> herd['one'] = Mammoth('Manfred Mammoth')
>>> herd['two'] = Mammoth('Joe Mammoth')
>>> herd['three'] = Mammoth('Marty the Wooly')
Search the herd:
>>> import zope.component
>>> from zope.catalog.interfaces import ICatalog
>>> catalog = zope.component.getUtility(ICatalog)
>>> mammoths = catalog.searchResults(full_name='Mammoth')
mammoths would be a list containing 'Manfred Mammoth' and 'Joe Mammoth' but not 'Marty the Wooly'
An Adapter takes an object providing an existing interface and extends it to provide a new interface.
The object providing the existing interface is passed to the Adapter in the constructor, and is stored in an attribute named ‘context. The source code for the grok.Adapter base class is simply:
class Adapter(object):
def __init__(self, context):
self.context = context
Base class to define an adapter. Adapters are automatically registered when a module is “grokked”.
Directives:
See also
See also
Optional. Identifies the name used for the adapter registration. If ommitted, no name will be used.
When a name is used for the adapter registration, the adapter can only be retrieved by explicitely using its name.
See also
See also
Example 1: Simple adaptation example
import grok
from zope import interface
class Cave(grok.Model):
"Cave is the class being adapted (the adaptee)"
def __init__(self, size=100):
self.size = size
class IHome(interface.Interface):
"IHome is the interface we want to add to a Cave"
def renovate():
"Enlarge Cave"
class CaveHome(grok.Adapter):
"Turns a Cave into a Home"
grok.context(Cave)
grok.implements(IHome) # the new interface provided by the adapter
def renovate(self):
# the adaptee is an attribute named 'context'
# and is passed in to the constructor
self.context.size += 10
# Adapation (component look-up) is invoked by passing the adaptee
# to the interface as a constructor and returns the component adapted to
home = IHome(cave)
home.renovate()
# Multiple adapters can exist that adapt and provide the same interfaces.
# They can be distinguished by name.
import zope.component
class LargeCaveHome(grok.Adapater):
"Turns a Cave in a large Home"
grok.context(Cave)
grok.implements(IHome)
grok.name('largehome')
def renovate(self):
self.context.size += 200
largehome = zope.component.getAdapter(cave, IHome, name='largehome')
largehome.renovate()
A MultiAdapter takes multiple objects providing existing interface(s) and extends them to provide a new interface.
The grok.MultiAdapter base class does not provide a default constructor implementation, it’s up to the individual multi-adapters to determine how to handle the objects being adapted.
Base class to define a Multi Adapter.
Directives:
Optional. Identifies the name used for the adapter registration. If ommitted, no name will be used.
When a name is used for the adapter registration, the adapter can only be retrieved by explicitely using its name.
Example: A home is made from a cave and a fireplace.
import grok
import zope.component
import zope.interface
class Fireplace(grok.Model): pass
class Cave(grok.Model): pass
class IHome(zope.interface.Interface): pass
class Home(grok.MultiAdapter):
grok.adapts(Cave, Fireplace)
grok.implements(IHome)
def __init__(self, cave, fireplace):
self.cave = cave
self.fireplace = fireplace
home = zope.component.getMultiAdapter((cave, fireplace), IHome)
Example: A Grok View is a MultiAdapter
In Grok, MultiAdapters are most commonly encountered in the form of Views. A View is a MultiAdapter which adapts the request and the context to provide the IGrokView interface. You can lookup a View component using the getMultiAdapter function.
def FireplaceView(grok.View):
grok.context(Fireplace)
grok.name('fire-view')
def AlternateFireplaceView(grok.View):
grok.context(Fireplace)
def render(self):
fireplaceview = zope.component.getMultiAdapter(
(self.context, self.request), IGrokView, name='fire-view'
)
return fireplaceview.render()
Annotation components are persistent writeable adapters.
Example: Storing annotations on model objects
import grok
from zope import interface
# Create a model and an interface you want to adapt it to
# and an annotation class to implement the persistent adapter.
class Mammoth(grok.Model):
pass
class ISerialBrand(interface.Interface):
unique = interface.Attribute("Brands")
class Branding(grok.Annotation):
grok.implements(IBranding)
unique = 0
# Grok the above code, then create some mammoths
manfred = Mammoth()
mumbles = Mammoth()
# creating Annotations work just like Adapters
livestock1 = ISerialBrand(manfred)
livestock2 = ISerialBrand(mumbles)
# except you can store data in them, this data will transparently persist
# in the database for as long as the object exists
livestock1.unique = 101
livestock2.unique = 102
# attributes not listed in the interface will also be persisted
# on the annotation
livestock2.foo = "something"
A global utility is an object which provides an interface, and can be looked-up by that interface and optionally the component name. The attributes provided by a global utility are not persistent.
Examples of global utilities are database connections, XML parsers, and web service proxies.
Base class to define a globally registered utility. Global utilities are automatically registered when a module is “grokked”.
Directives:
Optional. Identifies the name used for the adapter registration. If ommitted, no name will be used.
When a name is used for the global utility registration, the global utility can only be retrieved by explicitely using its name.
A local utility is an object which provides an interface, and can be looked-up by that interface and optionally the component name. The attributes provided by a local utility are transparently stored in the database (ZODB). This means that configuration changes to a local utility lasts between server restarts.
An example is for database connections or web service proxies, where you need to dynamically provide the connection settings so that they can be edited through-the-web.
Base class to define a utility that will be registered local to a grok.Site or grok.Application object by using the grok.local_utility() directive.
Directives:
Optional. Identifies the name used for the adapter registration. If ommitted, no name will be used.
When a name is used for the local utility registration, the local utility can only be retrieved by explicitely using its name.
Maybe required. If the local utility implements more than one interface or if the implemented interface cannot be determined, grok.provides() is required to disambiguate for what interface the local utility will be registered.
See also
Local utilities need to be registered in the context of grok.Site or grok.Application using the grok.local_utility() directive.
Contains a Site Manager. Site Managers act as containers for registerable components.
If a Site Manager is asked for an adapter or utility, it checks for those it contains before using a context-based lookup to find another site manager to delegate to. If no other site manager is found they defer to the global site manager which contains file based utilities and adapters.
Views handle interactions between the user and the model. The are constructed with context and request attributes, are responsible for providing a response. The request attribute in a View will always be for a normal HTTP Request.
The determination of what View gets used for what Model is made by walking the URL in the HTTP Request object sepearted by the / character. This process is called Traversal.
Base class to define a View.
Implements the grokcore.view.interfaces.IGrokView interface.
Construct URL.
If no arguments given, construct URL to view itself.
If only obj argument is given, construct URL to obj.
If only name is given as the first argument, construct URL to context/name.
If both object and name arguments are supplied, construct URL to obj/name.
Optionally pass a ‘data’ keyword argument which gets added to the URL as a cgi query string.
Returns a dictionary of namespaces that the template implementation expects to always be available.
This method is not intended to be overridden by application developers.
Returns a dictionary that is injected in the template namespace in addition to the default namespace.
This method is intended to be overridden by the application developer.
This method is meant to be implemented by grok.View subclasses. It will be called before the view’s associated template is rendered and can be used to pre-compute values for the template.
update() can take arbitrary keyword parameters which will be filled in from the request (in that case they must be present in the request).
A view can either be rendered by an associated template, or it can implement this method to render itself from Python. This is useful if the view’s output isn’t XML/HTML but something computed in Python (plain text, PDF, etc.)
render() can take arbitrary keyword parameters which will be filled in from the request (in that case they must be present in the request).
A ViewletManager is a component that provides access to a set of content providers (Viewlets). The ViewletManager’s responsibilities are:
- Aggregation of all viewlets registered for the manager.
- Apply a set of filters to determine the availability of the viewlets.
- Sort the viewlets based on some implemented policy. The default is to numerically sort accoring to the grok.order([number]) directive on a Viewlet.
- Provide an environment in which the viewlets are rendered.
- Render itself containing the HTML content of the viewlets.
ViewletManager’s also implement a read-only mapping API, so the Viewlet’s that they contain can be read like a normal Python dictionary.
Base class for a ViewletManager.
Example: Register a ViewletManager and Viewlet and use them from a template for a View
This is a very simple example, ViewletManagers and Viewlets can be used to support more complex HTML layout use cases, such as discriminating on the view or context in which a particular ViewletManager will be rendered. For example, a web site about caves and herds might want to show information in the sidebar specific to either a cave or a herd, depending upon whether a page is displaying information about a cave or a herd.
class ViewForACave(grok.View):
def render():
return grok.PageTemplate("""
<html><body>
<div tal:content="structure provider:cave" />
</body></html>
""")
class CaveManager(grok.ViewletManager):
grok.view(ViewForACave)
grok.name('cave')
class CaveViewlet(grok.Viewlet):
grok.order(30)
grok.viewletmanager(CaveManager)
def render(self):
return "Cave"
Viewlets are a flexible way to compound HTML snippets.
Viewlets are typically used for the layout of the web site. Often all the pages of the site have the same layout with header, one or two columns, the main content area and a footer.
Base class for a Viewlet.
Specialized View that returns data in JSON format.
Python data returned is automatically converted into JSON format using the simplejson library. Every method name in a grok.JSON component is registered as the name of a JSON View. The exceptions are names that begin with an _ or special names such as __call__. The grok.require decorator can be used to protect methods with a permission.
Example 1: Create a public and a protected JSON view.
class MammothJSON(grok.JSON):
"""
Returns JSON from URLs in the form of:
http://localhost/stomp
http://localhost/dance
"""
grok.context(zope.interface.Interface)
def stomp(self):
return {'Manfred stomped.': ''}
@grok.require('zope.ManageContent')
def dance(self):
return {'Manfred does not like to dance.': ''}
Specialized View for making web services that conform to the REST style. These Views can define methods named GET, PUT, POST and DELETE, which will be invoked based on the Request type.
Specialized View that responds to XML-RPC.
Example 1: Create a public and a protected XML-RPC view.
The grok.require decorator can be used to protect methods with a permission.
import grok
import zope.interface
class MammothRPC(grok.XMLRPC):
grok.context(zope.interface.Interface)
def stomp(self):
return 'Manfred stomped.'
@grok.require('zope.ManageContent')
def dance(self):
return 'Manfred doesn\'t like to dance.'
A Traverser is used to map from a URL to an object being published (Model) and the View used to interact with that object.
Base class for custom traversers. Override the traverse method to supply the desired custom traversal behaviour.
Returns an object and a sequence of names.
The publisher calls this method at the end of each traversal path. If the sequence of names is not empty, then a traversal step is made for each name. After the publisher gets to the end of the sequence, it will call browserDefault on the last traversed object.
The default behaviour in Grok is to return self.context for the object and ‘index’ for the default view name.
Note that if additional traversal steps are indicated (via a nonempty sequence of names), then the publisher will try to adjust the base href.
Lookup a name and return an object with self.context as it’s parent. The method can use the request to determine the correct object.
The ‘request’ argument is the publisher request object. The ‘name’ argument is the name that is to be looked up. It must be an ASCII string or Unicode object.
If a lookup is not possible, raise a NotFound error.
Example 1: Traverse into a Herd Model and return a Mammoth Model
import grok
class Herd(grok.Model):
def __init__(self, name):
self.name = name
class HerdTraverser(grok.Traverser):
grok.context(Herd)
def traverse(self, name):
return Mammoth(name)
class Mammoth(grok.Model):
def __init__(self, name):
self.name = name
Page Templates are the default templating system for Grok, they are an implementation of the Template Attribute Language (TAL). Page Templates are typically created from a string.
grok.PageTemplate("<h1>Hello World!</h1>")
Creates a Page Template from a filename.
grok.PageTemplateFile("my_page_template.pt")
Forms inherit from the grok.View class. They are a specialized type of View that renders an HTML Form.
Base class for forms.
The form’s form field definitions.
This attribute is used by many of the default methods.
The form’s widgets.
Set up the form’s widgets.
The default implementation uses the form definitions in the form_fields attribute and setUpInputWidgets.
The function should set the widgets attribute.
The default form validator
If an action is submitted and the action doesn’t have it’s own validator then this function will be called.
Return views of any errors.
The errors are returned as an iterable.
Save form data to an object.
This returns a dictionary with interfaces as keys and lists of field names as values to indicate which fields in which schemas had to be changed in order to save the data. In case the method works in update mode (e.g. on EditForms) and doesn’t have to update an object, the dictionary is empty.
Add forms are used for creating new objects. The widgets for this form are not bound to any existing content or model object.
Edit forms are used for editing existing objects. The widgets for this form are bound to the object set in the context attribute.
Display forms are used to display an existing object. The widgets for this form are bound to the object set in the context attribute.
Permissions are used to protect Views so that they can only be called by an authenticated principal. If a View in Grok does not have a grok.require directive declaring a permission needed to use the View, then the default anonymously viewable zope.View permission used.
Base class for permissions. You must specify a unique name for every permission using the grok.name directive. The convention for ensuring uniqueness is to prefix your permission name with the name of your Grok package followed by a dot, e.g. ‘mypackage.MyPermissionName’.
Directives:
grok.name(name)()
Required. Identifies the unique name (also used as the id) of the permission.
grok.title(title)()
Optional. Stored as the title attribute for this permission.
grok.description(description)()
Optional. Stored as the description attribute for this permission.
Example 1: Define a new Permission and use it to protect a View
import grok
import zope.interface
class Read(grok.Permission):
grok.name('mypackage.Read')
class Index(grok.View):
grok.context(zope.interface.Interface)
grok.require('mypackage.Read')
Roles provide a way to group together a collection of permissions. Principals (aka Users) can be granted a Role which will allow them to access all Views protected by the Permissions that the Role contains.
Base class for roles.
Directives:
grok.name(name)()
Required. Identifies the unique name (also used as the id) of the role.
grok.permissions(permissions)()
Required. Declare the permissions granted to this role. These can refer by permission class or by name.
grok.title(title)()
Optional. Stored as the title attribute for this role.
grok.description(description)()
Optional. Stored as the description attribute for this role.
Example 1: Define a new ‘paint.Artist’ Role and assign it to the ‘paint.grok’ principal
import grok
import zope.interface
class ViewPermission(grok.Permission):
grok.name('paint.ViewPainting')
class EditPermission(grok.Permission):
grok.name('paint.EditPainting')
class ErasePermission(grok.Permission):
grok.name('paint.ErasePainting')
class ApprovePermission(grok.Permission):
grok.name('paint.ApprovePainting')
class Artist(grok.Role):
"""
An Artist can view, create and edit paintings. However, they can
not approve their painting for display in the Art Gallery Cave.
"""
grok.name('paint.Artist')
grok.title('Artist')
grok.description('An artist owns the paintings that they create.')
grok.permissions(ViewPermission, EditPermission, ErasePermission)
# alternatively, use permission names
# grok.permissions(
# 'paint.ViewPainting', 'paint.EditPainting', 'paint.ErasePainting')
class CavePainting(grok.View):
grok.context(zope.interface.Interface)
grok.require(ViewPermission)
def render(self):
return 'What a beautiful painting.'
class EditCavePainting(grok.View):
grok.context(zope.interface.Interface)
grok.require(EditPermission)
def render(self):
return 'Let\'s make it even prettier.'
class EraseCavePainting(grok.View):
grok.context(zope.interface.Interface)
grok.require(ErasePermission)
def render(self):
return 'Oops, mistake, let\'s erase it.'
class ApproveCavePainting(grok.View):
grok.context(zope.interface.Interface)
grok.require(ApprovePermission)
def render(self):
return 'Painting owners cannot approve their paintings.'
# The app variable will typically be your Application instance,
# but could also be a container within your application.
from zope.securitypolicy.interfaces import IPrincipalRoleManager
IPrincipalRoleManager(app).assignRoleToPrincipal(
'paint.Artixt', 'paint.grok')