document adding/droping columns (fixes issue 104)

This commit is contained in:
Jan Dittberner 2011-10-29 21:59:11 +02:00
parent 1367004a17
commit 2bc520eaa6

View File

@ -9,8 +9,9 @@ Database schema versioning workflow
SQLAlchemy migrate provides the :mod:`migrate.versioning` API that is
also available as the :ref:`migrate <command-line-usage>` command.
Purpose of this package is frontend for migrations. It provides commands
to manage migrate repository and database selection aswell as script versioning.
Purpose of this package is frontend for migrations. It provides commands to
manage migrate :term:`repository` and database selection as well as script
versioning.
Project setup
@ -21,73 +22,76 @@ Project setup
Create a change repository
--------------------------
To begin, we'll need to create a *repository* for our
project.
To begin, we'll need to create a :term:`repository` for our project.
All work with repositories is done using the :ref:`migrate <command-line-usage>` command. Let's
create our project's repository::
All work with repositories is done using the :ref:`migrate
<command-line-usage>` command. Let's create our project's repository::
$ migrate create my_repository "Example project"
This creates an initially empty repository relative to current directory at
my_repository/ named `Example project`.
This creates an initially empty :term:`repository` relative to current
directory at :file:`my_repository/` named `Example project`.
The repository directory
contains a sub directory :file:`versions` that will store the :ref:`schema versions <changeset-system>`,
a configuration file :file:`migrate.cfg` that contains
:ref:`repository configuration <repository_configuration>` and a script :ref:`manage.py <project_management_script>`
that has the same functionality as the :ref:`migrate <command-line-usage>` command but is
preconfigured with repository specific parameters.
The :term:`repository` directory contains a sub directory :file:`versions` that
will store the :ref:`schema versions <changeset-system>`, a configuration file
:file:`migrate.cfg` that contains :ref:`repository configuration
<repository_configuration>` and a script :ref:`manage.py
<project_management_script>` that has the same functionality as the
:ref:`migrate <command-line-usage>` command but is preconfigured with
repository specific parameters.
.. note::
Repositories are associated with a single database schema,
and store collections of change scripts to manage that schema. The
scripts in a repository may be applied to any number of databases.
Each repository has an unique name. This name is used to identify the
repository we're working with.
Repositories are associated with a single database schema, and store
collections of change scripts to manage that schema. The scripts in a
:term:`repository` may be applied to any number of databases. Each
:term:`repository` has an unique name. This name is used to identify the
:term:`repository` we're working with.
Version control a database
--------------------------
Next we need to declare database to be under version control.
Information on a database's version is stored in the database
itself; declaring a database to be under version control creates a
table named **migrate_version** and associates it with your repository.
Next we need to declare database to be under version control. Information on a
database's version is stored in the database itself; declaring a database to be
under version control creates a table named **migrate_version** and associates
it with your :term:`repository`.
The database is specified as a `SQLAlchemy database url`_.
.. _`sqlalchemy database url`:
http://www.sqlalchemy.org/docs/05/dbengine.html#create-engine-url-arguments
http://www.sqlalchemy.org/docs/core/engines.html#database-urls
::
The :option:`version_control` command assigns a specified database with a
:term:`repository`::
$ python my_repository/manage.py version_control sqlite:///project.db my_repository
We can have any number of databases under this repository's version
control.
We can have any number of databases under this :term:`repository's
<repository>` version control.
Each schema has a version that SQLAlchemy Migrate manages. Each change
script applied to the database increments this version number. You can
see a database's current version::
Each schema has a :term:`version` that SQLAlchemy Migrate manages. Each change
script applied to the database increments this version number. You can retrieve
a database's current :term:`version`::
$ python my_repository/manage.py db_version sqlite:///project.db my_repository
0
A freshly versioned database begins at version 0 by default. This
assumes the database is empty. (If this is a bad assumption, you can
specify the version at the time the database is declared under version
control, with the "version_control" command.) We'll see that creating
and applying change scripts changes the database's version number.
A freshly versioned database begins at version 0 by default. This assumes the
database is empty or does only contain schema elements (tables, views,
constraints, indices, ...) that will not be affected by the changes in the
:term:`repository`. (If this is a bad assumption, you can specify the
:term:`version` at the time the database is put under version control, with the
:option:`version_control` command.) We'll see that creating and applying change
scripts changes the database's :term:`version` number.
Similarly, we can also see the latest version available in a
repository with the command::
Similarly, we can also see the latest :term:`version` available in a
:term:`repository` with the command::
$ python my_repository/manage.py version my_repository
0
We've entered no changes so far, so our repository cannot upgrade a
We've entered no changes so far, so our :term:`repository` cannot upgrade a
database past version 0.
Project management script
@ -95,36 +99,37 @@ Project management script
.. _project_management_script:
Many commands need to know our project's database url and repository
path - typing them each time is tedious. We can create a script for
our project that remembers the database and repository we're using,
and use it to perform commands::
Many commands need to know our project's database url and :term:`repository`
path - typing them each time is tedious. We can create a script for our project
that remembers the database and :term:`repository` we're using, and use it to
perform commands::
$ migrate manage manage.py --repository=my_repository --url=sqlite:///project.db
$ python manage.py db_version
0
The script manage.py was created. All commands we perform with it are
the same as those performed with the :ref:`migrate <command-line-usage>` tool, using the
repository and database connection entered above. The difference
between the script :file:`manage.py` in the current directory and the
script inside the repository is, that the one in the current directory
has the database URL preconfigured.
The script :file:`manage.py` was created. All commands we perform with it are
the same as those performed with the :ref:`migrate <command-line-usage>` tool,
using the :term:`repository` and database connection entered above. The
difference between the script :file:`manage.py` in the current directory and
the script inside the repository is, that the one in the current directory has
the database URL preconfigured.
.. note::
Parameters specified in manage.py should be the same as in :ref:`versioning api <versioning-api>`.
Preconfigured parameter should just be omitted from :ref:`migrate <command-line-usage>` command.
Parameters specified in manage.py should be the same as in :ref:`versioning
api <versioning-api>`. Preconfigured parameter should just be omitted from
:ref:`migrate <command-line-usage>` command.
Making schema changes
=====================
All changes to a database schema under version control should be done
via change scripts - you should avoid schema modifications (creating
tables, etc.) outside of change scripts. This allows you to determine
what the schema looks like based on the version number alone, and
helps ensure multiple databases you're working with are consistent.
All changes to a database schema under version control should be done via
change scripts - you should avoid schema modifications (creating tables, etc.)
outside of change scripts. This allows you to determine what the schema looks
like based on the version number alone, and helps ensure multiple databases
you're working with are consistent.
Create a change script
----------------------
@ -133,11 +138,12 @@ Our first change script will create a simple table
.. code-block:: python
account = Table('account', meta,
Column('id', Integer, primary_key=True),
Column('login', String(40)),
Column('passwd', String(40)),
)
account = Table(
'account', meta,
Column('id', Integer, primary_key=True),
Column('login', String(40)),
Column('passwd', String(40)),
)
This table should be created in a change script. Let's create one::
@ -152,62 +158,72 @@ Edit the change script
----------------------
Our change script predefines two functions, currently empty:
:func:`upgrade` and :func:`downgrade`. We'll fill those in
:py:func:`upgrade` and :py:func:`downgrade`. We'll fill those in:
.. code-block:: python
from sqlalchemy import *
from migrate import *
from sqlalchemy import Table, Column, Integer, String, MetaData
meta = MetaData()
account = Table('account', meta,
account = Table(
'account', meta,
Column('id', Integer, primary_key=True),
Column('login', String(40)),
Column('passwd', String(40)),
)
def upgrade(migrate_engine):
meta.bind = migrate_engine
account.create()
def downgrade(migrate_engine):
meta.bind = migrate_engine
account.drop()
As you might have guessed, :func:`upgrade` upgrades the database to the next
version. This function should contain the :ref:`schema changes<changeset-system>` we want to perform
(in our example we're creating a table).
.. note::
:func:`downgrade` should reverse changes made
by :func:`upgrade`. You'll need to write both functions for every change
script. (Well, you don't *have* to write downgrade, but you won't be
able to revert to an older version of the database or test your
scripts without it.)
The generated script contains * imports from sqlalchemy and migrate. You
should tailor the imports to fit your actual demand.
As you might have guessed, :py:func:`upgrade` upgrades the database to the next
version. This function should contain the :ref:`schema changes
<changeset-system>` we want to perform (in our example we're creating a
table).
:py:func:`downgrade` should reverse changes made by :py:func:`upgrade`. You'll
need to write both functions for every change script. (Well, you don't *have*
to write downgrade, but you won't be able to revert to an older version of the
database or test your scripts without it.) If you really don't want to support
downgrades it is a good idea to raise a :py:class:`NotImplementedError` or some
equivalent custom exception. If you let :py:func:`downgrade` pass silently you
might observe undesired behaviour for subsequent downgrade operations if
downgrading multiple :term:`versions <version>`.
.. note::
As you can see, **migrate_engine** is passed to both functions.
You should use this in your change scripts, rather
than creating your own engine.
As you can see, **migrate_engine** is passed to both functions. You should
use this in your change scripts, rather than creating your own engine.
.. warning::
You should be very careful about importing files from the rest of your
application, as your change scripts might break when your application
changes. More about `writing scripts with consistent behavior`_.
changes. Read more about `writing scripts with consistent behavior`_.
Test the change script
------------------------
Change scripts should be tested before they are committed. Testing a
script will run its :func:`upgrade` and :func:`downgrade` functions on a specified
database; you can ensure the script runs without error. You should be
testing on a test database - if something goes wrong here, you'll need
to correct it by hand. If the test is successful, the database should
appear unchanged after :func:`upgrade` and :func:`downgrade` run.
Change scripts should be tested before they are committed. Testing a script
will run its :func:`upgrade` and :func:`downgrade` functions on a specified
database; you can ensure the script runs without error. You should be testing
on a test database - if something goes wrong here, you'll need to correct it by
hand. If the test is successful, the database should appear unchanged after
:func:`upgrade` and :func:`downgrade` run.
To test the script::
@ -216,17 +232,31 @@ To test the script::
Downgrading... done
Success
Our script runs on our database (``sqlite:///project.db``, as
specified in manage.py) without any errors.
Our script runs on our database (:file:`sqlite:///project.db`, as specified in
:file:`manage.py`) without any errors.
Our repository's version is::
Our :term:`repository's <repository>` :term:`version` is::
$ python manage.py version
1
.. note::
Due to #41 the database must be exactly one :term:`version` behind the
:term:`repository` :term:`version`.
.. _production testing warning:
.. warning::
test command executes actual script, be sure you are NOT doing this on production database.
The :option:`test` command executes actual scripts, be sure you are *NOT*
doing this on production database.
If you need to test production changes you should:
#. get a dump of your production database
#. import the dump into an empty database
#. run :option:`test` or :option:`upgrade` on that copy
Upgrade the database
@ -235,12 +265,13 @@ Upgrade the database
Now, we can apply this change script to our database::
$ python manage.py upgrade
0 -> 1... done
0 -> 1...
done
This upgrades the database (``sqlite:///project.db``, as specified
when we created manage.py above) to the latest available version. (We
could also specify a version number if we wished, using the ``--version``
option.) We can see the database's version number has changed, and our
This upgrades the database (:file:`sqlite:///project.db`, as specified when we
created :file:`manage.py` above) to the latest available :term:`version`. (We
could also specify a version number if we wished, using the :option:`--version`
option.) We can see the database's :term:`version` number has changed, and our
table has been created::
$ python manage.py db_version
@ -248,110 +279,170 @@ table has been created::
$ sqlite3 project.db
sqlite> .tables
account migrate_version
sqlite> .schema account
CREATE TABLE account (
id INTEGER NOT NULL,
login VARCHAR(40),
passwd VARCHAR(40),
PRIMARY KEY (id)
);
Our account table was created - success!
Modifying existing tables
-------------------------
After we have initialized the database schema we now want to add another Column
to the `account` table that we already have in our schema.
First start a new :term:`changeset` by the commands learned above::
$ python manage.py script "Add email column"
This creates a new :term:`changeset` template. Edit the resulting script
:file:`my_repository/versions/002_Add_email_column.py`:
.. code-block:: python
from sqlalchemy import Table, MetaData, String, Column
def upgrade(migrate_engine):
meta = MetaData(bind=migrate_engine)
account = Table('account', meta, autoload=True)
emailc = Column('email', String(128))
emailc.create(account)
def downgrade(migrate_engine):
meta = MetaData(bind=migrate_engine)
account = Table('account', meta, autoload=True)
account.c.email.drop()
As we can see in this example we can (and should) use SQLAlchemy's schema
reflection (autoload) mechanism to reference existing schema objects. We could
have defined the table objects as they are expected before upgrade or downgrade
as well but this would have been more work and is not as convenient.
We can now apply the changeset to :file:`sqlite:///project.db`::
$ python manage.py upgrade
1 -> 2...
done
and get the following expected result::
$ sqlite3 project.db
sqlite> .schema account
CREATE TABLE account (
id INTEGER NOT NULL,
login VARCHAR(40),
passwd VARCHAR(40), email VARCHAR(128),
PRIMARY KEY (id)
);
Our account table was created - success! As our application evolves,
we can create more change scripts using a similar process.
Writing change scripts
======================
By default, change scripts may do anything any other SQLAlchemy
program can do.
As our application evolves, we can create more change scripts using a similar
process.
SQLAlchemy Migrate extends SQLAlchemy with several operations used to
change existing schemas - ie. ``ALTER TABLE`` stuff. See
:ref:`changeset <changeset-system>` documentation for details.
By default, change scripts may do anything any other SQLAlchemy program can do.
SQLAlchemy Migrate extends SQLAlchemy with several operations used to change
existing schemas - ie. ``ALTER TABLE`` stuff. See :ref:`changeset
<changeset-system>` documentation for details.
Writing scripts with consistent behavior
----------------------------------------
Normally, it's important to write change scripts in a way that's
independent of your application - the same SQL should be generated
every time, despite any changes to your app's source code. You don't
want your change scripts' behavior changing when your source code
does.
Normally, it's important to write change scripts in a way that's independent of
your application - the same SQL should be generated every time, despite any
changes to your app's source code. You don't want your change scripts' behavior
changing when your source code does.
.. warning::
**Consider the following example of what NOT to do**
Let's say your application defines a table in the :file:`model.py` file:
Let's say your application defines a table in the :file:`model.py` file:
.. code-block:: python
.. code-block:: python
from sqlalchemy import *
from sqlalchemy import *
meta = MetaData()
table = Table('mytable', meta,
Column('id', Integer, primary_key=True),
)
meta = MetaData()
table = Table('mytable', meta,
Column('id', Integer, primary_key=True),
)
... and uses this file to create a table in a change script:
... and uses this file to create a table in a change script:
.. code-block:: python
.. code-block:: python
from sqlalchemy import *
from migrate import *
import model
from sqlalchemy import *
from migrate import *
import model
def upgrade(migrate_engine):
model.meta.bind = migrate_engine
def upgrade(migrate_engine):
model.meta.bind = migrate_engine
def downgrade(migrate_engine):
model.meta.bind = migrate_engine
model.table.drop()
def downgrade(migrate_engine):
model.meta.bind = migrate_engine
model.table.drop()
This runs successfully the first time. But what happens if we change
the table definition in :file:`model.py`?
This runs successfully the first time. But what happens if we change the
table definition in :file:`model.py`?
.. code-block:: python
.. code-block:: python
from sqlalchemy import *
from sqlalchemy import *
meta = MetaData()
table = Table('mytable', meta,
Column('id', Integer, primary_key=True),
Column('data', String(42)),
)
meta = MetaData()
table = Table('mytable', meta,
Column('id', Integer, primary_key=True),
Column('data', String(42)),
)
We'll create a new column with a matching change script
We'll create a new column with a matching change script
.. code-block:: python
.. code-block:: python
from sqlalchemy import *
from migrate import *
import model
from sqlalchemy import *
from migrate import *
import model
def upgrade(migrate_engine):
model.meta.bind = migrate_engine
model.table.create()
def upgrade(migrate_engine):
model.meta.bind = migrate_engine
model.table.create()
def downgrade(migrate_engine):
model.meta.bind = migrate_engine
model.table.drop()
def downgrade(migrate_engine):
model.meta.bind = migrate_engine
model.table.drop()
This appears to run fine when upgrading an existing database - but the
first script's behavior changed! Running all our change scripts on a new
database will result in an error - the first script creates the table based
on the new definition, with both columns; the second cannot add the column
because it already exists.
This appears to run fine when upgrading an existing database - but the
first script's behavior changed! Running all our change scripts on a
new database will result in an error - the first script creates the
table based on the new definition, with both columns; the second
cannot add the column because it already exists.
To avoid the above problem, you should use SQLAlchemy schema reflection as
shown above or copy-paste your table definition into each change script
rather than importing parts of your application.
To avoid the above problem, you should copy-paste your table
definition into each change script rather than importing parts of your
application.
.. note:: Sometimes it is enough to just reflect tables with SQLAlchemy instead of copy-pasting - but remember, explicit is better than implicit!
.. note::
Sometimes it is enough to just reflect tables with SQLAlchemy instead
of copy-pasting - but remember, explicit is better than implicit!
Writing for a specific database
-------------------------------
Sometimes you need to write code for a specific database. Migrate
scripts can run under any database, however - the engine you're given
might belong to any database. Use engine.name to get the name of the
database you're working with
Sometimes you need to write code for a specific database. Migrate scripts can
run under any database, however - the engine you're given might belong to any
database. Use engine.name to get the name of the database you're working with
.. code-block:: python
@ -362,24 +453,24 @@ database you're working with
>>> engine.name
'sqlite'
Writings .sql scripts
---------------------
You might prefer to write your change scripts in SQL, as .sql files,
rather than as Python scripts. SQLAlchemy-migrate can work with that::
You might prefer to write your change scripts in SQL, as .sql files, rather
than as Python scripts. SQLAlchemy-migrate can work with that::
$ python manage.py version
1
$ python manage.py script_sql postgres
$ python manage.py script_sql postgresql
This creates two scripts
:file:`my_repository/versions/002_postgresql_upgrade.sql` and
:file:`my_repository/versions/002_postgresql_downgrade.sql`, one for
each *operation*, or function defined in a Python change script -
upgrade and downgrade. Both are specified to run with Postgres
databases - we can add more for different databases if we like. Any
database defined by SQLAlchemy may be used here - ex. sqlite,
postgres, oracle, mysql...
:file:`my_repository/versions/002_postgresql_downgrade.sql`, one for each
*operation*, or function defined in a Python change script - upgrade and
downgrade. Both are specified to run with PostgreSQL databases - we can add
more for different databases if we like. Any database defined by SQLAlchemy may
be used here - ex. sqlite, postgresql, oracle, mysql...
.. _command-line-usage:
@ -389,45 +480,49 @@ Command line usage
.. currentmodule:: migrate.versioning.shell
:command:`migrate` command is used for API interface. For list of commands and help use::
:command:`migrate` command is used for API interface. For list of commands and
help use::
$ migrate --help
$ migrate --help
:program:`migrate` command exectues :func:`main` function.
For ease of usage, generate your own :ref:`project management script <project_management_script>`,
which calls :func:`main` function with keywords arguments.
You may want to specify `url` and `repository` arguments which almost all API functions require.
:command:`migrate` command executes :func:`main` function.
For ease of usage, generate your own :ref:`project management script
<project_management_script>`, which calls :func:`main
<migrate.versioning.shell.main>` function with keywords arguments. You may want
to specify `url` and `repository` arguments which almost all API functions
require.
If api command looks like::
$ migrate downgrade URL REPOSITORY VERSION [--preview_sql|--preview_py]
$ migrate downgrade URL REPOSITORY VERSION [--preview_sql|--preview_py]
and you have a project management script that looks like
.. code-block:: python
from migrate.versioning.shell import main
from migrate.versioning.shell import main
main(url='sqlite://', repository='./project/migrations/')
main(url='sqlite://', repository='./project/migrations/')
you have first two slots filed, and command line usage would look like::
# preview Python script
$ migrate downgrade 2 --preview_py
# preview Python script
$ migrate downgrade 2 --preview_py
# downgrade to version 2
$ migrate downgrade 2
# downgrade to version 2
$ migrate downgrade 2
.. versionchanged:: 0.5.4
Command line parsing refactored: positional parameters usage
Command line parsing refactored: positional parameters usage
Whole command line parsing was rewriten from scratch with use of OptionParser.
Options passed as kwargs to :func:`~migrate.versioning.shell.main` are now parsed correctly.
Options are passed to commands in the following priority (starting from highest):
Options passed as kwargs to :func:`~migrate.versioning.shell.main` are now
parsed correctly. Options are passed to commands in the following priority
(starting from highest):
- optional (given by ``--some_option`` in commandline)
- optional (given by :option:`--some_option` in commandline)
- positional arguments
- kwargs passed to migrate.versioning.shell.main
- kwargs passed to :func:`migrate.versioning.shell.main`
Python API
@ -470,10 +565,10 @@ For example, the following commands are similar:
Experimental commands
=====================
Some interesting new features to create SQLAlchemy db models from
existing databases and vice versa were developed by Christian Simms
during the development of SQLAlchemy-migrate 0.4.5. These features are
roughly documented in a `thread in migrate-users`_.
Some interesting new features to create SQLAlchemy db models from existing
databases and vice versa were developed by Christian Simms during the
development of SQLAlchemy-migrate 0.4.5. These features are roughly documented
in a `thread in migrate-users`_.
.. _`thread in migrate-users`:
http://groups.google.com/group/migrate-users/browse_thread/thread/a5605184e08abf33#msg_85c803b71b29993f
@ -488,54 +583,59 @@ Here are the commands' descriptions as given by ``migrate help <command>``:
- ``make_update_script_for_model``: Create a script changing the old
Python model to the new (current) Python model, sending to stdout.
As this sections headline says: These features are EXPERIMENTAL. Take
the necessary arguments to the commands from the output of ``migrate
As this sections headline says: These features are *EXPERIMENTAL*. Take the
necessary arguments to the commands from the output of ``migrate
help <command>``.
Repository configuration
========================
SQLAlchemy-migrate repositories can be configured in their migrate.cfg
files. The initial configuration is performed by the `migrate create`
call explained in :ref:`Create a change repository
<create_change_repository>`. The following options are available
currently:
SQLAlchemy-migrate :term:`repositories <repository>` can be configured in their
:file:`migrate.cfg` files. The initial configuration is performed by the
`migrate create` call explained in :ref:`Create a change repository
<create_change_repository>`. The following options are available currently:
- `repository_id` Used to identify which repository this database is
- :option:`repository_id` Used to identify which repository this database is
versioned under. You can use the name of your project.
- `version_table` The name of the database table used to track the
schema version. This name shouldn't already be used by your
project. If this is changed once a database is under version
control, you'll need to change the table name in each database too.
- `required_dbs` When committing a change script, SQLAlchemy-migrate
will attempt to generate the sql for all supported databases;
normally, if one of them fails - probably because you don't have
that database installed - it is ignored and the commit continues,
perhaps ending successfully. Databases in this list MUST compile
successfully during a commit, or the entire commit will fail. List
the databases your application will actually be using to ensure your
updates to that database work properly. This must be a list;
example: `['postgres', 'sqlite']`
- :option:`version_table` The name of the database table used to track the
schema version. This name shouldn't already be used by your project. If this
is changed once a database is under version control, you'll need to change
the table name in each database too.
- :option:`required_dbs` When committing a change script, SQLAlchemy-migrate
will attempt to generate the sql for all supported databases; normally, if
one of them fails - probably because you don't have that database installed -
it is ignored and the commit continues, perhaps ending successfully.
Databases in this list MUST compile successfully during a commit, or the
entire commit will fail. List the databases your application will actually be
using to ensure your updates to that database work properly. This must be a
list; example: `['postgres', 'sqlite']`
- :option:`use_timestamp_numbering` When creating new change scripts, Migrate
will stamp the new script with a version number. By default this is
latest_version + 1. You can set this to 'true' to tell Migrate to use the UTC
timestamp instead.
.. versionadded:: 0.7.2
.. _custom-templates:
Customize templates
===================
Users can pass ``templates_path`` to API functions to provide customized templates path.
Path should be a collection of templates, like ``migrate.versioning.templates`` package directory.
Users can pass ``templates_path`` to API functions to provide customized
templates path. Path should be a collection of templates, like
``migrate.versioning.templates`` package directory.
One may also want to specify custom themes. API functions accept ``templates_theme`` for this purpose (which defaults to `default`)
One may also want to specify custom themes. API functions accept
``templates_theme`` for this purpose (which defaults to `default`)
Example::
/home/user/templates/manage $ ls
default.py_tmpl
pylons.py_tmpl
/home/user/templates/manage $ migrate manage manage.py --templates_path=/home/user/templates --templates_theme=pylons
/home/user/templates/manage $ ls
default.py_tmpl
pylons.py_tmpl
/home/user/templates/manage $ migrate manage manage.py --templates_path=/home/user/templates --templates_theme=pylons
.. versionadded:: 0.6.0