.. _zodb_with_zeo:
Using ZODB with ZEO
===================
:term:`ZODB` is a Python object persistence mechanism. :term:`ZODB`
works well as a storage mechanism for :mod:`pyramid` applications,
especially in applications that use :term:`traversal`.
:term:`ZEO` is an extension to ZODB which allows more than one process
to simultaneously communicate with a ZODB storage. Making a ZODB
database accessible to more than one process means that you can debug
your application objects at the same time that a :mod:`pyramid`
server that accesses the database is running, and will also allow your
application to run under multiprocess configurations, such as those
exposed by :term:`mod_wsgi`.
The easiest way to get started with ZODB in a :mod:`pyramid` application is
to use the ZODB ``pyramid_zodb`` paster template. See
:ref:`additional_paster_templates` for more information about using this
template. However, the Paster template does not set up a ZEO-capable
application. This chapter shows you how to do that "from scratch".
Installing Dependencies
-----------------------
#. Edit your :mod:`pyramid` application's ``setup.py`` file, adding
the following packages to the ``install_requires`` of the
application:
- ``repoze.folder``
- ``repoze.retry``
- ``repoze.tm2``
- ``repoze.zodbconn``
For example, the relevant portion of your application's
``setup.py`` file might look like so when you're finished adding
the dependencies.
.. code-block:: python
:linenos:
setup(
# ... other elements left out for brevity
install_requires=[
'pyramid',
'repoze.folder',
'repoze.retry',
'repoze.tm2',
'repoze.zodbconn',
],
# ... other elements left out for brevity
)
#. Rerun your application's ``setup.py`` file (e.g. using ``python
setup.py develop``) to get these packages installed. A number of
packages will be installed, including ``ZODB``. For the purposes
of this tutorial, we'll assume that your "application" is actually
just the result of the ``pyramid_starter`` Paster template.
Configuration
-------------
#. Edit your application's Paste ``development.ini`` file.
If you already have an ``app`` section in the ``.ini`` file named
``main``, rename this section to ``myapp`` (e.g. ``app:main`` ->
``app:myapp``). Add a key to it named ``zodb_uri``, e.g.
.. code-block:: ini
[app:myapp]
use = egg:myapp#app
zodb_uri = zeo://%(here)s/zeo.sock
reload_templates = true
debug_authorization = false
debug_notfound = false
If a ``pipeline`` named ``main`` does not already exist in the
paste ``.ini`` file , add a ``pipeline`` section named ``main``.
Put the names ``connector``, ``egg:repoze.retry#retry``, and
``egg:repoze.tm2#tm`` to the top of the pipeline.
.. code-block:: ini
[pipeline:main]
pipeline =
egg:repoze.retry#retry
egg:repoze.tm2#tm
myapp
When you're finished, your ``.ini`` file might look like so:
.. code-block:: ini
[DEFAULT]
debug = true
[app:myapp]
use = egg:myapp#app
zodb_uri = zeo://%(here)s/zeo.sock
reload_templates = true
debug_authorization = false
debug_notfound = false
[pipeline:main]
pipeline =
egg:repoze.retry#retry
egg:repoze.tm2#tm
myapp
[server:main]
use = egg:Paste#http
host = 0.0.0.0
port = 6543
See :ref:`MyProject_ini` for more information about project Paste
``.ini`` files.
#. Add a ``zeo.conf`` file to your package with the following
contents:
.. code-block:: text
%define INSTANCE .
address $INSTANCE/zeo.sock
read-only false
invalidation-queue-size 100
pid-filename $INSTANCE/zeo.pid
path $INSTANCE/myapp.db
blob-dir $INSTANCE/blobs
#. For the purposes of this tutorial we'll assume that you want your
:mod:`pyramid` application's :term:`root` object to be a
"folderish" object. To achieve this, change your application's
``models.py`` file to look like the below:
.. code-block:: python
from repoze.folder import Folder
class MyModel(Folder):
pass
def appmaker(root):
if not 'myapp' in root:
root['myapp'] = MyModel()
transaction.commit()
return root['myapp']
#. Change your application's ``__init__.py`` to look something like the
below:
.. code-block:: python
from pyramid.configuration import Configurator
from repoze.zodbconn.finder import PersistentApplicationFinder
from myapp.models import appmaker
import transaction
def app(global_config, **settings):
""" This function returns a ``pyramid`` WSGI
application.
It is usually called by the PasteDeploy framework during
``paster serve``"""
# paster app config callback
zodb_uri = settings['zodb_uri']
finder = PersistentApplicationFinder(zodb_uri, appmaker)
def get_root(request):
return finder(request.environ)
config = Configurator(root_factory=get_root, settings=settings)
# .. other configuration statements ..
return config.make_wsgi_app()
Running
-------
#. Start the ZEO server in a terminal with the current directory set
to the package directory:
.. code-block:: text
../bin/runzeo -C zeo.conf
You should see something like this, as a result:
.. code-block:: text
:linenos:
[chrism@snowpro myapp]$ ../bin/runzeo -C zeo.conf
------
2009-09-19T13:48:41 INFO ZEO.runzeo (9910) created PID file './zeo.pid'
# ... more output ...
2009-09-19T13:48:41 INFO ZEO.zrpc (9910) listening on ./zeo.sock
#. While the ZEO server is running, start the application server:
.. code-block:: text
:linenos:
[chrism@snowpro myapp]$ ../bin/paster serve myapp.ini
Starting server in PID 10177.
serving on 0.0.0.0:6543 view at http://127.0.0.1:6543
#. The root object is now a "folderish" ZODB object. Nothing else
about the application has changed.
#. You can manipulate the database directly (even when the
application's HTTP server is running) by using the ``pshell``
command in a third terminal window:
.. code-block:: text
:linenos:
[chrism@snowpro sess]$ ../bin/paster --plugin=pyramid pshell \
myapp.ini myapp
Python 2.5.4 (r254:67916, Sep 4 2009, 02:12:16)
[GCC 4.2.1 (Apple Inc. build 5646)] on darwin
Type "help" for more information. "root" is the Pyramid app root object.
>>> root
>>> root.foo = 'bar'
>>> import transaction
>>> transaction.commit()