作者: Desmond Chen,
在本文中, 将会介绍Django-south的基本概念和基本用法, 帮助Django开发人员简化数据迁移的过程. 在django-admin命令中有syncdb指令, 其目的是根据model.py创建相应的数据库表. 但我们在开发的过程中, 经常会需要更改model, 删除或者增加Field, 这时, syncsb命令就不那么好用了, 因为syncsb无法自动更改数据库表结构. 因此, 我们时常需要手动删除数据库表, 再运行syncdb.
而有了south后, 情况就不同了. south为django带来了数据迁移功能, 它的主要目的是为django应用程序带来一个简单, 稳定, 与数据库管理系统无关的迁移层, 用来自动处理django中的数据表变化. 使用south提提供的工具, 你可以在不同版本的数据库结构中来回迁移,
south刚出来时只是一个相对无名的数据库迁移应用. 但渐渐地它成为了django中最为流行和普遍使用的数据迁移工具. 并且在django 1.7发布时, 数据迁移功能将会被整合到django中, 其提供的django-admin migrate也将代替syncdb命令. 因此, 我们不得不了解一下south的魅力所在.
1. 安装South
安装south的方式有许多中, 包括从pip安装, 直接使用south的Mercurial库安装, 或从操作系统自带的软件库中安装. 推荐使用pip, 将south安装到VirtualEnv中:
pip install South
然后在django的'south'添加到setting.py的INSTALLED_APPS中, 并运行syncdb, 创建south所需要的数据表:
./manage.py syncdb
2. 在新建的App中使用South
a. 前期准备
首先我们介绍在新app中使用south. 需要注意的是, south的迁移记录文件是储存在每个app下的migrations目录中的, 如果该app没有创建任何migrations, 那么该app还是通过syncdb来管理的.
创建新app, 并将其添加到INSTALLED_APPS中:
./manage.py startapp southtut
打开该app的model.py, 创建model:
from django.db import models
class Knight(models.Model):
name = models.CharField(max_length=100)
of_the_round_table = models.BooleanField()
b. 第一次迁移
south提供了多种建立迁移记录的方式, 有些是自动的, 有些是手动的. 而用的最多的可能就是--auto和--initial这两种自动创建的方式了.
--auto是根据之前的迁移记录, 与当前model作比较, 然后自动创建新的迁移记录. 例如当添加了一个新field时, --auto会注意到, 并自动生成添加了新栏的迁移记录.
你会注意到, --auto需要上一次的迁移记录才能创建新的迁移记录. 而--initial则可以用来为model中所有的表创建初始的迁移记录. 因此, 先使用--initial初始化迁移记录, 然后在model有所变动后, 使用--auto自动生成迁移记录
使用--initial为我们新建的app southtut创建初始迁移记录:
$ ./manage.py schemamigration southtut --initial
Creating migrations directory at '/home/andrew/Programs/litret/southtut/migrations'...
Creating __init__.py in '/home/andrew/Programs/litret/southtut/migrations'...
+ Added model southtut.Knight
Created 0001_initial.py. You can now apply this migration with: ./manage.py migrate southtut
c. 之后的迁移
此时, 在southtut的migrations下就自动生成reated 0001_initial.py文件, 这就是第一个迁移记录文件. 注意, 此时打开MySQL或PostreSQL, 你会发现没有名为southtut_knight的数据表, 因为数据库中的数据表并没有变化(未创建也未修改), 我们需要用到migrate命令来完成这一步:
$ ./manage.py migrate southtut
Running migrations for southtut:
- Migrating forwards to 0001_initial.
> southtut:0001_initial
- Loading initial data for southtut.
此时再打开MySQL或PostgreSQL, southtut_knight数据表已经创建好了. 但到现在为止, 这些功能都是sync能实现的. 接下来我们修改Knight model, 增加一个field:
from django.db import models
class Knight(models.Model):
name = models.CharField(max_length=100)
of_the_round_table = models.BooleanField()
dances_whenever_able = models.BooleanField()
此时如果使用syncsb, 则django无法为你增加dances_whenever_able列, 因此, 我们使用south:
$ ./manage.py schemamigration southtut --auto
+ Added field dances_whenever_able on southtut.Knight
Created 0002_auto__add_field_knight_dances_whenever_able.py. You can now apply this migration with: ./manage.py migrate southtut
从输出的提示可以看到, south已经为你增加了新的迁移记录文件(0002_auto__add_field_knight_dances_whenever_able.py), south的命名方式是序列号+所做的更改. 接下来, 我们根据提示, 使用migrate命令修改数据库:
$ ./manage.py migrate southtut
Running migrations for southtut:
- Migrating forwards to 0002_auto__add_field_knight_dances_whenever_able.
> southtut:0002_auto__add_field_knight_dances_whenever_able
- Loading initial data for southtut.
3. 创建新的没有默认值的field
当修改model时, 创建没有默认值(default)的field后:
from django.db import models
class Knight(models.Model):
name = models.CharField(max_length=100)
of_the_round_table = models.BooleanField()
dances_whenever_able = models.BooleanField()
shrubberies = models.IntegerField(null=False)
此时再执行schemamigration, 会出现一些没有见过的选择:
./manage.py schemamigration southtut --auto
? The field 'Knight.shrubberies' does not have a default specified, yet is NOT NULL.
? Since you are adding or removing this field, you MUST specify a default
? value to use for existing rows. Would you like to:
? 1. Quit now, and add a default to the field in models.py
? 2. Specify a one-off value to use for existing columns now
? Please select a choice:
其中选项1意思是, 放弃本次自动迁移, 并退出, 你可以在field中添加default值后再执行schemamigration. 选项2的意思是, 为已经存在的行添加一个一次性的值. 当你选择2时, 会出现python提示行, 你可以使用python的datetime模块:
? Please select a choice: 2
? Please enter Python code for your one-off default value.
? The datetime module is available, so you can do e.g. datetime.date.today()
>>> 0
+ Added field shrubberies on southtut.Knight
Created 0003_auto__add_field_knight_shrubberies.py. You can now apply this migration with: ./manage.py migrate southtut
此时你可以查看自动生成的迁移记录文件, south会为新建的栏添加默认值0, 这样数据库才不会报错. 然后我们再执行migrate:
$ ./manage.py migrate southtut
Running migrations for southtut:
- Migrating forwards to 0003_auto__add_field_knight_shrubberies.
> southtut:0003_auto__add_field_knight_shrubberies
- Loading initial data for southtut.mou
4. --fake的使用
在团队开发时, 可能会遇到这样的问题: 你的代码已经和migrate之后保持一样. 那么这时你无法再使用python manage.py migrate, south会提示 无法合并, 此时我们就需要用到:
python manage.py migrate --fake 0003