.. _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()