Direct Usage¶
The main entry point is the DatabaseManager class which accepts the following parameters:
- database: location of database (URL string, dictionary, or
peewee.Database
instance). - table_name: table name to store migration history (default: migration_history)
- directory: directory to store migration files (default: migrations)
from peewee import SqliteDatabase
from peewee_moves import DatabaseManager
manager = DatabaseManager(SqliteDatabase('test.sqlite'))
manager = DatabaseManager({'engine': 'peewee.SqliteDatabase', 'name': 'test.sqlite'})
manager = DatabaseManager('sqlite:///test.sqlite')
From there, you can call methods to manage and run migration files.
New Migration¶
This will create a blank migration file with the next ID and the default name of “auto migration” or whatever name you provide
>>> manager.revision()
created: INFO: 0001_auto_migration
>>> manager.revision('custom name')
created: INFO: 0002_custom_name
Database Upgrade¶
This will run the upgrade() method in each unapplied migration. If you specify a target, the migrator will only run upgrades through that target. If no target is specified, all unapplied migrations will run.
>>> manager.upgrade('0001')
INFO: upgrade: 0001_auto_migration
>>> manager.upgrade()
INFO: upgrade: 0002_custom_name
INFO: upgrade: 0003_another_migration
Pass the –fake flag to add the record to the migration history table but don’t actually run the migration:
>>> manager.upgrade(fake=True)
INFO: upgrade: 0003_another_migration
>>> manager.upgrade('0001', fake=True)
INFO: upgrade: 0002_custom_name
INFO: upgrade: 0001_auto_migration
Database Downgrade¶
This does the opposite of upgrade(). It calls the downgrade() method on each applied migration. If you specify a target, the migrator will only run downgrades through that target. If no target is specified, only the most recent migration will be downgraded.
>>> manager.downgrade()
INFO: downgrade: 0003_another_migration
>>> manager.downgrade('0001')
INFO: downgrade: 0002_custom_name
INFO: downgrade: 0001_auto_migration
Pass the –fake flag to remove the record from migration history table but don’t actually run the migration:
>>> manager.downgrade(fake=True)
INFO: downgrade: 0003_another_migration
>>> manager.downgrade('0001', fake=True)
INFO: downgrade: 0002_custom_name
INFO: downgrade: 0001_auto_migration
Delete Migration¶
This will remove a migration from the database and the filesystem, as if it never happened. You might never need this, but it could be useful in some circumstances.
>>> manager.delete('0003')
INFO: deleted: 0003_another_migration
Migration Status¶
This will simply show the status of each migration file so you can see which ones have been applied.
>>> manager.status()
INFO: [x] 0001_auto_migration
INFO: [x] 0002_custom_name
INFO: [ ] 0003_another_migration
Automagic Migration Creation¶
It’s possible to create a migration file automatically that will have the operations necessary to upgrade and downgrade your existing models.
Let’s say you have the following two models defined in models.py:
import peewee
class Group(peewee.Model):
code = peewee.IntegerField(unique=True)
name = peewee.CharField(max_length=250)
class Meta:
db_table = 'auth_groups'
class User(peewee.Model):
name = peewee.CharField(max_length=250)
group = peewee.ForeignKeyField(Group, related_name='users')
class Meta:
db_table = 'auth_users'
indexes = (
(('name', 'group'), True),
)
Running the following command will create the migration file necessary to upgrade/downgrade the Group model.
>>> migrator.create('models.Group')
INFO: created: 0001_create_table_auth_groups
You can also pass a module to create migration files for all models within:
>>> migrator.create('models')
INFO: created: 0001_create_table_auth_groups
INFO: created: 0002_create_table_auth_users
Let’s look at both those files:
0001_create_table_auth_groups.py
def upgrade(migrator):
with migrator.create_table('auth_groups') as table:
table.primary_key('id')
table.int('code', unique=True)
table.char('name', max_length=250)
def downgrade(migrator):
migrator.drop_table('auth_groups')
0002_create_table_auth_users.py
def upgrade(migrator):
with migrator.create_table('auth_users') as table:
table.primary_key('id')
table.char('name', max_length=250)
table.foreign_key('int', 'group_id', references='auth_groups.id')
table.add_index(('name', 'group_id'), unique=True)
def downgrade(migrator):
migrator.drop_table('auth_users')
As you can see, this creates all the operations necessary to create the table for both models.
The user model has a foreign key to the groups model, but the migration file for users does not contain a dependency on the Group model! This is intentional. If the Group model changes or gets removed in a future migration, this migration will not be impacted and can still run any time a new database needs to be set up.
This currently only supports creating models. If your model changes, it’s up to you to write the migration to support that.
Migrator API¶
The previous exmple shows the files that were created automatically to support two models. The argument to upgrade() and downgrade() is a migrator instance that has a database-agnostic API. This allows you to write command in Python that will get executed against the database when upgrade() and downgrade() are called.
Here’s a full example of everything you can do in either upgrade() or downgrade() using the migrator API:
with migrator.create_table(name, safe=False) as table:
table.primary_key('colname', **kwargs)
table.bare('colname', **kwargs)
table.biginteger('colname', **kwargs)
table.binary('colname', **kwargs)
table.blob('colname', **kwargs)
table.bool('colname', **kwargs)
table.date('colname', **kwargs)
table.datetime('colname', **kwargs)
table.decimal('colname', **kwargs)
table.double('colname', **kwargs)
table.fixed('colname', **kwargs)
table.float('colname', **kwargs)
table.integer('colname', **kwargs)
table.char('colname', **kwargs)
table.text('colname', **kwargs)
table.time('colname', **kwargs)
table.uuid('colname', **kwargs)
table.foreign_key('coltype', 'colname', references='othertable.col')
table.add_index(('col1', 'col2'), unique=True)
table.add_constraint('constraint string')
migrator.drop_table('name', safe=False, cascade=False)
migrator.add_column('table', 'name', 'type', **kwargs)
migrator.drop_column('table', 'name', 'field', cascade=True)
migrator.rename_column('table', 'old_name', 'new_name')
migrator.rename('table', 'old_name', 'new_name')
migrator.add_not_null('table', 'column')
migrator.drop_not_null('table', 'column')
migrator.add_index('table', 'columns', unique=False)
migrator.drop_index('table', 'index_name')
cursor = migrator.execute_sql(sql, params=None)
The kwargs are passed to the field as they would be if you were defining the field on a Peewee model class.
The migrator.execute_sql allows for writing direct SQL if you need to. There’s nothing stopping you from writing something specific to your database engine using this method.
And remember, the migration files are just Python! So you can import and run other Python code if needed.
Command Line Usage¶
A command named peewee-db
is automatically installed with this package.
This command allows you to easily issue database management commands without using the Python
API directly:
$ peewee-db --help
Usage: peewee-db [OPTIONS] COMMAND [ARGS]...
Run database migration commands.
Options:
--directory TEXT [required]
--database TEXT [required]
--table TEXT
--help Show this message and exit.
Commands:
create Create a migration based on an existing...
delete Delete the target migration from the...
downgrade Run database downgrades.
info Show information about the current database.
revision Create a blank migration file.
status Show information about migration status.
upgrade Run database upgrades.
Each command requires that you specify a database
and directory
where
database
is the URL to your database and directory
is where migration files are stored.
For example, here’s how you can show the status of your database:
$ peewee-db --database=sqlite:///mydata.sqlite --directory=migrations status
INFO: [ ] 0001_create_table_auth_groups
INFO: [ ] 0002_create_table_auth_users
And to create a new revision file you can do this:
$ peewee-db --database=sqlite:///mydata.sqlite --directory=migrations revision "custom revision"
INFO: created: 0003_custom_revision
Pass the –fake flag to upgrade or downgrade to update the migration history table without running the migration:
Flask Usage¶
This package includes an interface to Flask versions 0.11 or later using Click which provides an easy-to-use command line interface. If you are using Flask 0.10, you can use backported integration via Flask-CLI.
For this to work properly, you must define a configuration variable named DATABASE
in your
Flask app config:
app = Flask(__name__)
app.config['DATABASE'] = 'sqlite:///database.sqlite'
This can be a connection string as shown above, or also a dict or peewee.Database
instance.
app.config['DATABASE'] = SqliteDatabase('test.sqlite')
app.config['DATABASE'] = {
'engine': 'peewee.SqliteDatabase',
'name': 'test.sqlite'
}
The db
command will automatically add the command to the cli if Flask is installed:
flask db --help
This gives you the following command line interface:
$ flask db --help
Usage: flask db [OPTIONS] COMMAND [ARGS]...
Run database migration commands for a Flask application.
Options:
--help Show this message and exit.
Commands:
create Create a migration based on an existing model.
delete Delete the target migration from the filesystem and database.
downgrade Run database downgrades.
info Show information about the current database.
revision Create a blank migration file.
status Show information about the database.
upgrade Run database upgrades.
This should look very similar since it uses the same commands we just looked at!
For example, to create the migration for User model would look like this:
$ flask db create models.User
INFO: created: 0003_create_table_user
And to create a blank migration with a custom name would look like this:
$ flask db revision "custom revision"
INFO: created: 0004_custom_revision