document adding/droping columns (fixes issue 104)
This commit is contained in:
parent
1367004a17
commit
2bc520eaa6
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user