Grok tutorial
=============

Welcome to the Grok tutorial! Grok is an extension to Zope 3 that
makes it much quicker and easier to develop web applications with Zope
3, *without* losing the power and flexibility of Zope 3 itself. Grok
builds on existing Zope 3 technology like the component architecture
but exposes it in a different way to the developer; a way which we
believe makes developing with Zope 3 easier and more fun.

In this tutorial we will show you the various things you can do with
Grok. We'll start out simple, and will slowly go to more complex usage
patterns. All you're expected to know is the Python programming
language and an understanding of web programming. It also helps to be
familiar with Zope Page Templates, though most of the examples should
be fairly obvious even if you do not.

.. sidebar:: Getting started with Zope Page Templates

  You can find introductions and more information about Zope Page
  Templates (ZPT, sometimes also called TAL) in various places:

    http://plone.org/documentation/tutorial/zpt

    http://wiki.zope.org/ZPT/FrontPage

  Note that some of the information in these introductions may refer
  to concepts not available in Grok or Zope 3, in particular variables
  like ``here`` or ``template``. The basic principles will work with
  Zope 3 (and Grok) however; try reading ``context`` or ``view``
  instead.

Setting up grokproject
----------------------

Setting up grok on a Unix-like (Linux, Mac OS X) environment is
easy. 

Let's go through the prerequisites first. You need a computer
connected to the internet, as Grok installs itself over the
network. You also need Python 2.4 installed.

Because Grok uses a source distribution of Zope 3, you may need to
install your operating system's Python "dev" package. You also need a
working C compiler (typically ``gcc``) installed, as we compile bits of
Zope 3 during setup. Finally, you also need ``easy_install`` installed
so it becomes easy to install eggs.

.. sidebar:: Installing ``easy_install``

  If you don't already have ``easy_install`` available, you can find the
  script to set it up here:

    http://peak.telecommunity.com/DevCenter/EasyInstall#installing-easy-install

  You need to download `ez_setup.py`_. Then, you run it like this to
  install ``easy_install`` into your system Python::

    $ sudo python2.4 ez_setup.py

  .. _`ez_setup.py`: http://peak.telecommunity.com/dist/ez_setup.py

  This will make ``easy_install`` available to you.

  **Note**: Sometimes you have ``easy_install`` installed but you need
  a newer version of the underlying setuptools infrastructure to make
  Grok work. You can automatically upgrade setuptools this by doing::

    $ sudo easy_install -U setuptools

Once you are done with the prerequisites, you can install
grokproject itself::

  $ sudo easy_install grokproject

We're ready to create our first grok project now!

Creating a grok project
-----------------------

Let's create a first Grok project. A Grok project is a working
environment for a developer using Grok. In essence, a directory with a
lot of files and subdirectories in it. Let's create a Grok project
called Sample::

  $ grokproject Sample

This tells grokproject to create a new subdirectory called ``Sample``
and set up the project in there. grokproject will automatically
download and install Zope 3 and Grok into the project area.  If you
already have Zope 3.3.x installed, you can save some time and tell
grokproject to reuse that installation:

  $ grokproject --with-zope3=/usr/local/Zope-3.3.1 Sample

grokproject will tell you what it will be creating::

  Selected and implied templates:
    grokproject#grokproject  A grok project

  Variables:
    egg:      Sample
    package:  sample
    project:  Sample

The "Selected and implied templates" line is something reported by
Paste, which is the Python project generation software which
grokproject is using. After this, it reports three names. 

First, it reports the name this project will have if in the project's
``setup.py``::

    egg:      Sample

Next, it specifies the name of the Python package that you will be
developing with. The package will be placed under the project's ``src``
directory::

    package:  sample

Finally, it gives the name of the project directory that it will
create (under the current directory)::

    project:  Sample

You will be asked a number of questions now. First you need to supply
the name of the initial module that your package will contain. We'll
stick with the default ``app.py``::
 
  Enter module (Name of a demo Python module placed into the package) ['app.py']:

After this Grok asks you for an initial username and password for the
Zope server. We'll use ``grok`` for both::

  Enter user (Name of an initial administrator user): grok
  Enter passwd (Password for the initial administrator user): grok

Now you have to wait a while as grokproject downloads Grok and
possibly Zope 3 and sets up the project environment for you.

After all that, Grok, along with a Zope 3 instance, is ready to go. 

Starting up Zope
----------------

You can go into the ``Sample`` project directory now::

  $ cd Sample

A Zope 3 instance has been installed in the 'parts/instance'
directory. You can start it (into the foreground) by typing the
following::

  $ parts/instance/bin/zopectl fg

This will make Zope 3 available on port 8080, and you can log in with
username ``grok`` and password ``grok``. Assuming you've started up
Zope on your localhost, you can go to it here:

  http://localhost:8080

This first pops up a login dialog (username: grok and password:
grok). It will then show a simple Grok admin interface. This allows
you to install new Grok applications. Our sample application
(sample.app.Sample) will be available for adding.

Installing our sample application won't have much of an effect yet; if
you do so and select it, you will get a "This page is not available
yet" message.

You can shut down Zope 3 at any time by typing ``ctrl-c``. Shut it down
now. We will be shutting down and starting up Zope 3 often in this
tutorial.

An empty Grok project
---------------------

Let's take a closer look at what's been created in the Sample project
directory.

One of the things grokproject created was a ``setup.py`` file. This file
contains information about your project. This information is used by
Python distutils and setuptools. You can use the ``setup.py`` file to
upload your project to the Python Cheeseshop. We will discuss this in
more detail later in this tutorial. (XXX)

We have already seen the ``parts`` directory. This directory contains
all software installed by grokproject that is not a simple Python
library. The only part interesting to us right now is the ``instance``
directory, which contains the ``zopectl`` script to start up Zope which
we used before.

.. sidebar:: What about the other directories and files in our project?

  What about the other files and subdirectories in our ``Sample`` project
  directory? Grokproject sets up the project using a system called
  `zc.buildout`_. The ``eggs``, ``develop-eggs`` and ``bin``
  directories are all set up and maintained by zc.buildout. See its
  documentation for more information about how to use it. The
  configuration of the project and its dependency is in
  ``buildout.cfg``. For now, you can avoid these details however.

  .. _`zc.buildout`: http://cheeseshop.python.org/pypi/zc.buildout

The actual code of the project will all be inside the ``src``
directory. In it is a Python package directory called ``sample`` with
the ``app.py`` file that grokproject said it would create. Let's look
at this file::

  import grok
  
  class Sample(grok.Application, grok.Model):
      pass

Not very much yet, but enough to make an installable Grok
application. We'll go into the details of what this means later.

Besides this, there is an empty ``__init__.py`` file to make this
directory a Python package.

What's left is a ``configure.zcml`` file. Unlike in typical Zope 3
applications, this will only ever contain a single line that registers
this application with Zope 3 which means we can completely ignore it::

  <grok package="." xmlns="http://namespaces.zope.org/grok" />

Publishing a simple web page
----------------------------

Let's publish a simple static web page. Grok is geared towards web
applications and therefore not really meant for publishing a large
number of static (pregenerated) web pages. For that you're better off
to use a specialized system such as Apache. But, to start a developing
a web application we need to be able to put some simple HTML on the
web, first.

As you might've seen previously, our ``Sample`` application doesn't have
a front page yet. Let's create one. 

To do this, you need to create a new directory in ``src/sample/`` called
``app_templates``. This directory will contain the HTML used for
anything defined in the ``app`` module. Grok knows to associate the
directory to the module by its name (``<module_name>_templates``).

In this directory we will place the ``index`` template for our ``Sample``
application object. To do this, create a file in the ``app_templates``
directory called ``index.pt``.

The ``.pt`` extension indicates that this file is a Zope Page Template
(ZPT). This allows us to make the page dynamic later on.

Put the following (very simplistic) HTML in the file ``index.pt``::

  <html>
  <body>
  <p>Hello world!</p>
  </body>
  </html>

We're done with the template for now. Now we need to tell Grok to
actually use this template. To do this, modify ``src/sample/app.py`` so
that it reads like this::

  import grok

  class Sample(grok.Application, grok.Model):
      pass

  class Index(grok.View):
      pass

As you can see, all we did was add a class called ``Index`` that
subclasses from ``grok.View``. This indicates to Grok that we want a
view named ``index`` for the application. A *view* is a way to view
some content; in this case installations of our ``Sample``
application. 

The empty class definition above is enough for Grok to go look in the
``app_templates`` directory for ``index.pt``.

Let's try this out. Restart Zope (``ctrl-C`` and then
``parts/instance/bin/zopectl fg`` from your Sample project
directory). Go to the Grok admin page:

  http://localhost:8080

and add a Sample application. Give it the name ``test``.

You can now go to the installed application if you click on its link. This
will bring you to the following URL:

  http://localhost:8080/test

You should see a simple web page with the following text on it::

  Hello world!

A second view
-------------

Since our view is named `index` we've done something slightly special:
it's the default view of our application object. We can also still
access it explicitly by naming the view:

  http://localhost:8080/test/index

If you view that URL in your browser, you should see the same result
as before.

Often, your application needs more than one view. A document for
instance may have an ``index`` view that displays it, but another
``edit`` view to change its contents.

To create a second view, create another template called ``bye.pt`` in
``app_templates``. Make it have the following content::

  <html>
  <body>
  <p>Bye world!</p>
  </body>
  </html>

We need to tell Grok that this template is available as a view. Modify
``app.py`` to read like this::

  import grok

  class Sample(grok.Application, grok.Model):
      pass

  class Index(grok.View):
      pass

  class Bye(grok.View):
      pass

Restart Zope. You can now go to a new web page called ``bye``:

  http://localhost:8080/test/bye

When you load this web page in a browser, you should see the following
text::

  Bye world!

The rule for each view is that your template should have the same name
as the class, but lowercased and with the ``.pt`` postfix.

Making our page dynamic
-----------------------

Static web pages are not very helpful if we want to make a dynamic web
application. Let's make a page that shows the result of a very simple
calculation: ``1 + 1``. 

We will use a Zope Page Templates (ZPT) directive to do this
calculation inside ``index.pt`` template. Change the ``index.pt`` to
read like this::

  <html>
  <body>
  <p tal:content="python: 1 + 1">this is replaced</p>
  </body>
  </html>

We've used the ``tal:content`` page template directive to replace the
content between the ``<p>`` and ``</p>`` tags with something else, in
this case the result of the Python expression ``1 + 1``.

Since restarting Zope is not necessary for changes that are limited to
the page templates, you can just reload the web page:

  http://localhost:8080/test

You should see the following result::

  2

Looking at the source of the web page shows us this::

  <html>
  <body>
  <p>2</p>
  </body>
  </html>

As you can see, the content of the ``<p>`` tag was indeed replaced
with the result of the expression ``1 + 1``.

Static resources for our web page
---------------------------------

In real-world web pages, we almost never publish a web page that just
contains bare-bones HTML. We also want to refer to other resources,
such as images, CSS files or Javascript files. As an example, let's
add some style to our web page.

To do this, create a new directory called ``static`` in the ``sample``
package (so, ``src/sample/static``). In it, place a file called
``style.css`` and put in the following content::

  body {
    background-color: #FF0000;
  }

In order to use it, we also need to refer to it from our
``index.pt``. Change the content of ``index.pt`` to read like this::

  <html>
  <head>
  <link rel="stylesheet" type="text/css" 
        tal:attributes="href static/style.css" />
  </head>
  <body>
  <p>Hello world!</p>
  </body>
  </html>

Now restart Zope and reload the page:

  http://localhost:8080/test

The web page should now show up with a red background.

You will have noticed we used the ``tal:attributes`` directive in our
``index.pt`` now. This uses Zope Page Templates to dynamically
generate the link to our file ``style.css``.

Let's take a look at the source code of the generated web page::

  <html>
  <link rel="stylesheet" type="text/css"
        href="http://localhost:8080/test/@@/sample/style.css" />
  <body>
  <p>Hello world!</p>
  </body>
  </html>

As you can see, the ``tal:attributes`` directive is gone and has been
replaced with the following URL to the actual stylesheet:

  http://localhost:8080/test/@@/sample/style.css

We will not go into the details of the structure of the URL here, but
we will note that because it's generated this way, the link to
``style.css`` will continue to work no matter where you install your
application (i.e. in a virtual hosting setup).

Pulling in images or javascript is very similar. Just place your image
files and `.js` files in the ``static`` directory, and create the URL
to them using ``static/<filename>`` in your page template.

Using view methods
------------------

ZPT is deliberately limited in what it allows you to do with Python.
It is good application design practice to use ZPT for fairly simple
templating purposes only, and to do anything a bit more complicated in
Python code. Using ZPT with arbitrary Python code is easy: you just
add methods to your view class and use them from your template.

Let's see how this is done by making a web page that displays the
current date and time. We will use our Python interpreter to find out
what works::

  $ python
  Python 2.4.4
  Type "help", "copyright", "credits" or "license" for more information.
  >>> 

We will need Python's ``datetime`` class, so let's import it::

  >>> from datetime import datetime

Note that this statement brings us beyond the capabilities of simple
ZPT use; it is not allowed to import arbitrary Python modules from
within a ZPT template; only Python *expressions* (with a result) are
allowed, not *statements* such as ``from .. import ..``.

Let's get the current date and time::

  >>> now = datetime.now()

This gives us a date time object; something like this::

  >>> now
  datetime.datetime(2007, 2, 27, 17, 14, 40, 958809)

Not very nice to display on a web page, so let's turn it into a
prettier string using the formatting capabilities of the ``datetime``
object::

  >>> now.strftime('%Y-%m-%d %H:%M')
  '2007-02-27 17:14'

That looks better.

So far nothing new; just Python. We will integrate this code into our
Grok project now. Go to ``app.py`` and change it to read like this::

  import grok
  from datetime import datetime

  class Sample(grok.Application, grok.Model):
      pass

  class Index(grok.View):    
      def current_datetime(self):
          now = datetime.now()
          return now.strftime('%Y-%m-%d %H:%M')

.. sidebar:: Unassociated templates

  If you have followed the tutorial so far, you will have an extra
  template called ``bye.pt`` in your ``app_templates`` directory.
  Since in the above code sample we have no more class using it, the
  ``bye.pt`` template will have become *unassociated**. When you try
  to restart Zope, grok will be unable to read your application, and
  Zope will crash with an error message like this::

    GrokError: Found the following unassociated template(s) when
    grokking 'sample.app': bye.  Define view classes inheriting from
    grok.View to enable the template(s).

  To resolve this error, simply remove ``bye.pt`` from your
  ``app_templates`` directory.

We've simply added a method to our view that returns a string
representing the current date and time. Now to get this string in our
page template. Change ``index.pt`` to read like this::

  <html>
  <body>
  <p tal:content="python:view.current_datetime()"></p>
  </body>
  </html>

Restart Zope. This is needed as we changed the content of a Python
file (``app.py``). Now reload our index page to see whether it worked:

  http://localhost:8080/test

You should see a web page with a date and time like this on your
screen now::

  2007-02-27 17:21

What happened here? When viewing a page, the view class (in this case
``Index`` is instantiated by Zope. The name ``view`` in the template
is always made available and is associated with this instance. We then
simply call the method on it in our template.

There is another way to write the template that is slightly shorter
and may be easier to read in some cases, using a ZPT path expression::

  <html>
  <body>
  <p tal:content="view/current_datetime"></p>
  </body>
  </html>

Running this has the same result as before.

Generating HTML from Python
---------------------------

While usually you will be using templates to generate HTML, sometimes
you want to generate complicated HTML in Python and then include it in
an existing web page. For reasons of security against cross-site
scripting attacks, TAL will automatically escape any HTML into `&gt;`
and `&lt;`. With the ``structure`` directive, you can tell TAL
explicitly to not escape HTML this way, so it is passed literally into
the template. Let's see how this is done. Change ``app.pt`` to read like
this::

  import grok

  class Sample(grok.Application, grok.Model):
      pass

  class Index(grok.View):
      def some_html(self):
          return "<b>ME GROK BOLD</b>"

and then change ``index.pt`` to read like the following::

  <html>
  <body>
  <p tal:content="structure python:view.some_html()"></p>
  </body>
  </html>

Let's take another look at our web page:

  http://localhost:8080/test

You should see the following text (in bold):

  **ME GROK BOLD**

This means the HTML we generated from the ``some_html`` method was
indeed successfully integrated in our web page.  Without the the
``structure`` directive, you would've seen the following instead::
 
  <b>ME GROK BOLD</b>

Completely Python-driven views
------------------------------

.. sidebar:: Setting the content-type

  When generating the complete content of a page yourself, it's often
  useful to change the content-type of the page to something else than
  ``text/plain``. Let's change our code to return simple XML and set
  the content type to ``text/xml``::

    import grok

      class Sample(grok.Application, grok.Model):
          pass

      class Index(grok.View):
          def render(self):
              self.response.setHeader('Content-Type',
                                      'text/xml; charset=UTF-8')
              return "<doc>Some XML</doc>"

  Views in Grok have a ``response`` object assigned to it.

Sometimes it is inconvenient to have to use a template at all. Perhaps
we are not returning a HTML page at all, for instance. In this case,
we can use the special ``render`` method on a view.

Modify ``app.py`` so it reads like this::

   import grok

   class Sample(grok.Application, grok.Model):
       pass

   class Index(grok.View):
       def render(self):
           return "ME GROK NO TEMPLATE"

If you were to start up Zope with an ``index.pt`` template still
inside ``app_templates`` you would get an error::

    GrokError: Multiple possible ways to render view <class
    'sample.app.Index'>. It has both a 'render' method as well as an
    associated template.

In the face of ambiguity Grok, like Python, refuses to guess. To
resolve this error, remove ``index.pt`` from the ``app_templates``
directory.

Now take another look at our test application:

  http://localhost:8080/test

You should see the following::

  ME GROK NO TEMPLATE

You should see this even when you view the source of the page. When
looking at the content type of this page, you will see that it is
``text/plain``.

Doing some calculation before viewing a page
--------------------------------------------

Instead of calculating some values in a method call from the template,
it is often more useful to calculate just before the web page's
template is calculated. This way you are sure that a value is only
calculated once per view, even if you use it multiple times.

You can do this by defining an ``update`` method on the view class. Modify
``app.py`` to read like this::

  import grok

  class Sample(grok.Application, grok.Model):
      pass

  class Index(grok.View):
      def update(self):
          self.alpha = 2 ** 8

This sets a name ``alpha`` on the view just before the template is
being displayed, so we can use it from the template. You can set as
many names on ``self`` as you like.

Now we need a template ``index.pt`` that uses ``alpha``::

  <html>
  <body>
  <p tal:content="view/alpha"></p>
  </body>
  </html>

Restart Zope and then let's take another look at our application:

  http://localhost:8080/test

You should see 256, which is indeed 2 raised to the power 8.

Reading URL parameters
----------------------

When developing a web application, you don't just want to output data,
but also want to use input. One of the simplest ways for a web
application to receive input is by retrieving information as a URL
parameter. Let's devise a web application that can do sums for us. In
this application, if you enter the following URL into that
application:

  http://localhost:8080/test?value1=3&value2=5

you should see the sum (8) as the result on the page. 

Modify ``app.py`` to read like this::

  import grok

  class Sample(grok.Application, grok.Model):
      pass

  class Index(grok.View):
      def update(self, value1, value2):
          self.sum = int(value1) + int(value2)

We need an ``index.pt`` that uses ``sum``::

  <html>
  <body>
  <p tal:content="view/sum"></p>
  </body>
  </html>

Restart Zope. Now going to the folllowing URL should display 8:

  http://localhost:8080/test?value1=3&value2=5

Other sums work too, of course:

  http://localhost:8080/test?value1=50&value2=50

What if we don't supply the needed parameters (``value1`` and
``value2``) to the request? We get an error:

  http://localhost:8080/test

You can look at the window where you started up Zope to see the error
traceback. This is the relevant complaint::

  TypeError: Missing argument to update(): value1

We can modify our code so it works even without input for either parameter::

  import grok

  class Sample(grok.Application, grok.Model):
      pass

  class Index(grok.View):
      def update(self, value1=0, value2=0):
          self.sum = int(value1) + int(value2)

Restart Zope, and see it can now deal with missing parameters (they
default to ``0``).

Simple forms
------------

Entering the parameters through URLs is not very pretty. Let's use a
form for this instead. Change ``index.pt`` to contain a form, like
this::

  <html>
  <body>
  <form tal:attributes="action view/url" method="GET">
    Value 1: <input type="text" name="value1" value="" /><br />
    Value 2: <input type="text" name="value2" value="" /><br />
    <input type="submit" value="Sum!" />
  </form>
  <p>The sum is: <span tal:replace="view/sum">sum</span></p>
  </body>
  </html>

One thing to note here is that we dynamically generate the form's
``action``. We make the form submit to itself, basically. Grok views
have a special method called ``url`` that you can use to retrieve the
URL of the view itself (and other URLs which we'll go into later).

Leave the ``app.py`` as in the previous section, for now. You can now
go to the web page::

  http://localhost:8080/test

You can submit the form with some values, and see the result displayed
below.

We still have a few bugs to deal with however. For one, if we don't fill
in any parameters and submit the form, we get an error like this::

  File "../app.py", line 8, in update
    self.sum = int(value1) + int(value2)
  ValueError: invalid literal for int(): 

This is because the parameters were empty strings, which cannot be
converted to integers. Another thing that is not really pretty is that
it displays a sum (0) even if we did not enter any data. Let's change
``app.py`` to take both cases into account::

  import grok

  class Sample(grok.Application, grok.Model):
      pass

  class Index(grok.View):
      def update(self, value1=None, value2=None):
          try:
              value1 = int(value1)
              value2 = int(value2)
          except (TypeError, ValueError):
              self.sum = "No sum yet"
              return
          self.sum = value1 + value2

We catch any TypeError and ValueError here so that wrong or missing
data does not result in a failure. Instead we display the text "No sum
yet". If we don't get any error, the conversion to integer was fine,
and we can display the sum.

Restart Zope and go to the form again to try it out:

  http://localhost:8080/test

A view for a model
------------------

So far, we have only seen views that do the work all by themselves.
In typical applications this is not the case however - views display
information that is stored elsewhere. In Grok applications, views work
for content object; subclasses of ``grok.Model`` or
``grok.Container``.

Our ``Sample`` class is a ``grok.Model``, so let's use this to
demonstrate the basic principle. Let's modify ``app.py`` so that
``Sample`` actually makes some data available::

  import grok

  class Sample(grok.Application, grok.Model):
      def information(self):
          return "This is important information!"

  class Index(grok.View):
      pass

In this case, the information (``"This is important information!"``)
is just hardcoded, but you can imagine information is retrieved from
somewhere else, such as a relational database or the filesystem.

We now want to display this information in our template ``index.pt``::

  <html>
  <body>
  <p tal:content="python:context.information()">replaced</p>
  </body>
  </html>

Restart Zope. When you view the page:

  http://localhost:8080/test

You should now see the following::

  This is important information!

Previously we have seen that you can access methods and attributes on
the view using the special ``view`` name in a template. Similarly, the
name ``context`` is also available in each template. ``context``
allows us to access information on the context object the view is
displaying. In this case this is an instance of ``Sample``, our
application object.

Separating the model from the view that displays it is an important
concept in structuring applications. The view, along with the
template, is responsible for displaying the information and its user
interface. The model represents the actual information the application
is about, such as documents, blog entries or wiki pages.

Because grok structures applications this way, the way the model is
displayed can be changed without modifying the model itself.

Let's do that by making the view do something to the information. Change 
``app.py`` again::

  import grok

  class Sample(grok.Application, grok.Model):
      def information(self):
          return "This is important information!"
    
  class Index(grok.View):
      def reversed_information(self):
          return ''.join(reversed(self.context.information()))

You can see that it is possible to access the context object (an
instance of ``Sample``) from within the view class, by accessing the
``context`` attribute.

What we do here is reverse the string returned from the
``information()`` method. You can try it on the Python prompt::

  >>> ''.join(reversed('foo'))
  'oof'

Now let's modify the ``index.pt`` template so that it uses the
``reversed_information`` method::

  <html>
  <body>
  <p>The information: 
    <span tal:replace="python:context.information()">info</span>
  </p>
  <p>The information, reversed: 
    <span tal:replace="python:view.reversed_information()">info</span>
  </p>
  </body>
  </html>

Restart Zope. When you view the page:

  http://localhost:8080/test

You should now see the following:

  The information: This is important information!

  The information, reversed: !noitamrofni tnatropmi si sihT 

Storing data
------------

So far we have only displayed either hardcoded data, or calculations
based on end-user input. What if we actually want to *store* some
information, such as something the user entered? The easiest way to do
this with Zope is to use the Zope Object Database (ZODB).

The ZODB is a database of Python objects. You can store any Python
object in it, though you do need to follow a few simple rules (the
"rules of persistence", which we will go into later). Our ``Sample``
application object is stored in the object database, so we can store
some information on it.

Let's create an application that stores a bit of text for us. We will
use one view to view the text (``index``) and another to edit it
(``edit``).

Modify ``app.py`` to read like this::

  import grok

  class Sample(grok.Application, grok.Model):
      text = 'default text'

  class Index(grok.View):
      pass

  class Edit(grok.View):
      def update(self, text=None):
          if text is None:
             return
          self.context.text = text

The ``Sample`` class gained a class attribute with some default text.
In the ``update`` method of the ``Edit`` view you can see we actually
set the ``text`` attribute on the context, if at least a ``text``
value was supplied by a form. This will set the ``text`` attribute on
the instance of the ``Sample`` object in the object database, and thus
will override the default ``text`` class attribute.

Change the ``index.pt`` template to read like this::

  <html>
  <body>
  <p>The text: <span tal:replace="python:context.text">text</span></p>
  </body>
  </html>

This is a very simple template that just displays the ``text``
attribute of the ``context`` object (our ``Sample`` instance).

Create an ``edit.pt`` template with the following content::

  <html>
  <body>
  <form tal:attributes="action view/url" method="POST">
  Text to store: <input type="text" name="text" value="" /><br />
  <input type="submit" value="Store" />
  </form>
  </body>
  </html>

This template display a form asking for a bit of text. It submits to
itself.

Restart Zope. Let's first view the index page:

  http://localhost:8080/test

You should see ``default text``.

Now let's modify the text by doing to the edit page of the application:

  http://localhost:8080/test/edit

Type in some text and press the "Store" button. Since it submits to
itself, we will see the form again, so go to the index page manually:

  http://localhost:8080/test
 
You should now see the text you just entered on the page. This means
that your text was successfully stored in the object database!

You can even restart Zope and go back to the index page, and your text
should still be there.

Redirection
-----------

Let's make our application a bit easier to use. First, let's change
``index.pt`` so it includes a link to the edit page. To do this, we
will use the ``url`` method on the view::

  <html>
  <body>
  <p>The text: <span tal:replace="python:context.text">text</span></p>
  <p><a tal:attributes="href python:view.url('edit')">Edit this page</a></p>
  </body>
  </html>

Giving ``url`` a single string argument will generate a URL to the
view named that way on the same object (``test``), so in this case
``test/edit``.

Now let's change the edit form so that it redirects back to the
``index`` page after you press the submit button::

  import grok

  class Sample(grok.Application, grok.Model):
      text = 'default text'

  class Index(grok.View):
      pass

  class Edit(grok.View):
      def update(self, text=None):
          if text is None:
              return
          self.context.text = text
          self.redirect(self.url('index'))

The last line is the new one. We use the ``url`` method on the view to
construct a URL to the ``index`` page. Since we're in the template, we
can simply call ``url`` on ``self``. Then, we pass this to another
special method available on all ``grok.View`` subclasses,
``redirect``. We tell the system to redirect to the ``index`` page.

Form value
----------

Let's change our application so it displays what we last entered in
the edit form as well, not just in on the index page. 

To make this work, change edit.pt so it reads like this::

  <html>
  <body>
  <form tal:attributes="action view/url" method="POST">
  Text to store: <input type="text" name="text" tal:attributes="value python:context.text" value="" /><br />
  <input type="submit" value="Store" />
  </form>
  </body>
  </html>

The only change is that we have used ``tal:attributes`` to include the
value of the ``text`` attribute of the context object in the form.

While we explain here on how to do this manually here, there is also
an automatic way to do this, with many more features. For much more on
this, see section XXX on EditForms.

The rules of persistence
------------------------

These are the "rules of persistence":

* You should subclass classes that want to store data from
  ``persistent.Persistent`` so that it's easy to store them in the
  ZODB. The simplest way to do this with Grok is to subclass from
  ``grok.Model`` or ``grok.Container``.

* Instances that you want to store should be connected to other
  persistent classes that are already stored. The simplest way to do
  this with Grok is to attach them somehow to the ``grok.Application``
  object, directly or indirectly. This can be done by setting them as
  an attribute, or by putting them in a container (if you made your
  application subclass ``grok.Container``).

* To make sure that the ZODB knows you changed a mutable attribute
  (such as a simple Python list or dictionary) in your instance, set
  the special ``_p_changed`` attribute on that instance to
  ``True``. This is only necessary if that attribute is not
  ``Persistent`` itself. It is also not necessary when you create or
  overwrite an attribute directly using ``=``.

If you construct your application's content out of ``grok.Model`` and
``grok.Container`` subclasses you mostly follow the rules
already. Just remember to set ``_p_changed`` in your methods if you
find yourself modifying a Python list (with ``append``, for instance)
or dictionary (by storing a value in it).

The code in the section `Storing data`_ is a simple example. As you can
see there, we have to do nothing special to obey the rules of
persistence in this case.

If we use a mutable object instead, we do need to take special action. Let's
change our example code to use a mutable object::

  import grok

  class Sample(grok.Application, grok.Model):
      text = 'default text'

  class Index(grok.View):
      pass

  class Edit(grok.View):
      def update(self, text=None):
          if text is None:
              return
          self.context.texts.append(text)
          self.redirect(self.url('index'))

XXX

Containers
----------

Let's now turn our application into a container, and place some
objects into it. Let's turn our application into a container. For now,
think of a container as a Python dictionary, but one that knows about
the rules of persistence.

Changing the application object into a container will have some larger
repercussions: we need to throw away our original ``test`` application
object in the database and create a new one.

TDB

Events
------

TDB

Constructing urls with ``view.url()``
-------------------------------------

TDB

Putting your project into SVN
-----------------------------

TDB

Uploading your project to the Python Cheeseshop
-----------------------------------------------

TDB
