Not Logged In

zope.generations 4.0.0a1

Generations are a way of updating objects in the database when the application
schema changes. An application schema is essentially the structure of data,
the structure of classes in the case of ZODB or the table descriptions in the
case of a relational database.

Generations are a way of updating objects in the database when the application
schema changes. An application schema is essentially the structure of data,
the structure of classes in the case of ZODB or the table descriptions in the
case of a relational database.

When you change your application’s data structures, for example,
you change the semantic meaning of an existing field in a class, you will
have a problem with databases that were created before your change. For a
more thorough discussion and possible solutions, see
http://wiki.zope.org/zope3/DatabaseGenerations

We will be using the component architecture, and we will need a database and a
connection:

Here’s some generations-specific code. We will create and register a
SchemaManager. SchemaManagers are responsible for the actual updates of the
database. This one will be just a dummy. The point here is to make the
generations module aware that our application supports generations.

The default implementation of SchemaManager is not suitable for this test
because it uses Python modules to manage generations. For now, it
will be just fine, since we don’t want it to do anything just yet.

The consequence of this action is that now the database contains the fact
that our current schema number is 0. When we update the schema, Zope3 will
have an idea of what the starting point was. Here, see?

Back to the story. Some time passes and one of our clients gets hacked because
we forgot to escape HTML special characters! The horror! We must fix this
problem ASAP without losing any data. We decide to use generations to impress
our peers.

Let’s update the schema manager (drop the old one and install a new custom
one):

We have set minimum_generation to 1. That means that our application
will refuse to run with a database older than generation 1. The generation
attribute is set to 2, which means that the latest generation that this
SchemaManager knows about is 2.

evolve() is the workhorse here. Its job is to get the database from
generation-1 to generation. It gets a context which has the attribute
‘connection’, which is a connection to the ZODB. You can use that to change
objects like in this example.

In this particular implementation generation 1 escapes the answers (say,
critical, because they can be entered by anyone!), generation 2 escapes the
questions (say, less important, because these can be entered by authorized
personell only).

In fact, you don’t really need a custom implementation of ISchemaManager. One
is available, we have used it for a dummy previously. It uses Python modules
for organization of evolver functions. See its docstring for more information.

In real life you will have much more complex object structures than the one
here. To make your life easier, there are two very useful functions available
in zope.generations.utility: findObjectsMatching() and
findObjectsProviding(). They will dig through containers recursively to help
you seek out old objects that you wish to update, by interface or by some other
criteria. They are easy to understand, check their docstrings.

Because evolveMinimumSubscriber is very lazy, it only updates the database just
enough so that your application can use it (to the minimum_generation, that
is). Indeed, the marker indicates that the database generation has been bumped
to 1:

>>> root[generations_key]['some.app']
1

We see that generations are working, so we decide to take the next step
and evolve to generation 2. Let’s see how this can be done manually:

Default behaviour of evolve upgrades to the latest generation provided by
the SchemaManager. You can use the how argument to evolve() when you want
just to check if you need to update or if you want to be lazy like the
subscriber which we have called previously.

Frequently subsystems used to compose an application rely on other
subsystems to operate properly. If both subsystems provide schema
managers, it is often helpful to know the order in which the evolvers
will be invoked. This allows a framework and it’s clients to be able
to evolve in concert, and the clients can know that the framework will
be evolved before or after itself.

This can be accomplished by controlling the names of the schema
manager utilities. The schema managers are run in the order
determined by sorting their names.

Let’s assume that for some reason each of these subsystems needs to
add a generation, and that generation 1 of ‘another.app-extension’
depends on generation 1 of ‘another.app’. We’ll need to provide
schema managers for each that record that they’ve been run so we can
verify the result:

In the the example above, we manually initialized the answers. We
shouldn’t have to do that manually. The application should be able to
do that automatically.

IInstallableSchemaManager extends ISchemaManager, providing an install
method for performing an intial installation of an application. This
is a better alternative than registering database-opened subscribers.

Generations key (stored in database root) has been changed from
zope.app.generations to zope.generations. Migration is done when
evolve is run the first time by coping the exisiting generations data
over to the new key. So the old and the new key can be used in parallel.