迁移
将迁移添加到应用
如果应用程序已经有模型和数据库表,但是没有迁移,则需要将其转换为使用迁移:
python manage.py makemigrations your_app_label
执行python manage.py migrate - fake-initial 。Django将检测到一个初始迁移和要创建的表已经存在,并将标记为已应用迁移。(没有--fake-initial标志,migrate命令将错误输出,因为要创建的表已存在
数据迁移
使用迁移来更改数据库中的数据,如果需要,还可以与模式一起使用。
更改数据的迁移通常称为“数据迁移”;他们最好写成单独的迁移,坐在模式迁移。
创建空的迁移文件:
python manage.py makemigrations --empty yourappname
打开生成的迁移文件:
from django.db import models, migrations class Migration(migrations.Migration): dependencies = [ ('yourappname', '0001_initial'), ] operations = [ ]
创建一个新函数,并使用RunPython。RunPython需要一个可调用作为其参数,它需要两个参数 - 第一个是一个app registry,它将所有模型的历史版本加载到其中以匹配历史记录迁移就位,第二个是SchemaEditor,您可以使用它手动实现数据库模式更改(但要小心,这样做可能会使迁移自动检测器混乱)
用first_name和last_name的组合值填充新的name字段,使用历史模型并在行上进行迭代:
def combine_names(apps, schema_editor): # We can't import the Person model directly as it may be a newer MyModel = apps.get_model('demo1', 'MyModel') for m in MyModel.objects.all(): m.name = '%s %s' % (m.first_name, m.last_name) m.save() class Migration(migrations.Migration): dependencies = [ ('demo1', '0001_initial'), ] operations = [ migrations.RunPython(combine_names), ]
正常运行python manage.py migrate与其他迁移一起放置
通过其他应用访问模型
在下面的示例中,我们在app1中有一个迁移,需要使用app2中的模型。我们不关心move_m1的细节,而是需要从两个应用程序访问模型的事实。因此,我们添加了一个依赖关系,指定app2的最后一次迁移:
class Migration(migrations.Migration): dependencies = [ ('app1', '0001_initial'), # added dependency to enable using models from app2 in move_m1 ('app2', '0004_foobar'), ] operations = [ migrations.RunPython(move_m1), ]
模式操作
自定义编写迁移文件API
CreateModel
class CreateModel(name, fields, options=None, bases=None, managers=None)¶
在项目历史中创建一个新模型,并在数据库中创建一个相应的表来匹配它。
name是model的名称,如写在models.py文件中。
fields是(field_name, field_instance)的2元组的列表。字段实例应为未绑定字段(因此只有models.CharField(),而不是一个字段从另一个模型)。
options是来自模型的Meta类的值的可选字典。
bases是此模型继承的其他类的可选列表;它可以包含类对象以及格式为"appname.ModelName"的字符串,如果你想依赖另一个模型(所以你从历史版本继承)。如果没有提供,它默认只继承标准的models.Model。
managers获取(manager_name, manager_instance)的2元组列表。列表中的第一个管理器将成为迁移期间此模型的默认管理器。
migrations.CreateModel( name='Author', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=50, unique=True)), ('email', models.EmailField(max_length=254, unique=True)), ], ),
RunSQL
class RunSQL(sql, reverse_sql=None, state_operations=None, hints=None)¶
允许在数据库上运行任意SQL - 对于Django不能直接支持的数据库后端的更高级功能(如部分索引)非常有用。
sql和reverse_sql(如果提供)应为在数据库上运行的SQL字符串。在大多数数据库后端(除PostgreSQL之外),Django将在执行它们之前将SQL拆分为单独的语句。这需要安装sqlparse Python库。
详细参考官方文档
RunPython
class RunPython(code, reverse_code=None, atomic=True, hints=None)
在历史上下文中运行自定义Python代码。code(和reverse_code如果提供)应该是接受两个参数的可调用对象;第一个是包含与项目历史中的操作位置匹配的历史模型的django.apps.registry.Apps的实例,第二个是SchemaEditor的实例。
详细参考官方文档
模式编辑器
没看懂是干嘛的,后面有时间在研究研究
编写数据库迁移
数据迁移和多数据库
在使用多个数据库时,需要解决是否针对某个特定数据库运行迁移。比如说你 只是 想在特定的数据库上做迁移。
为此你可以在RunPython中通过查看schema_editor.connection.alias 属性来检查数据库连接别名:
from django.db import migrations def forwards(apps, schema_editor): if not schema_editor.connection.alias == 'default': return # Your migration code goes here class Migration(migrations.Migration): dependencies = [ # Dependencies to other migrations ] operations = [ migrations.RunPython(forwards), ]
New in Django 1.8.
你也可以提供一个提示作为 **hints参数传递到数据库路由的allow_migrate() 方法:
myapp/dbrouters.py
class MyRouter(object): def allow_migrate(self, db, app_label, model_name=None, **hints): if 'target_db' in hints: return db == hints['target_db'] return True
然后,要在你的迁移中利用,执行以下操作:
from django.db import migrations def forwards(apps, schema_editor): # Your migration code goes here class Migration(migrations.Migration): dependencies = [ # Dependencies to other migrations ] operations = [ migrations.RunPython(forwards, hints={'target_db': 'default'}), ]
如果你的RunPython或者RunSQL操作只对一个模型有影响,最佳实践是将model_name作为提示传递,使其尽可能对路由可见。这对可复用的和第三方应用极其重要。
添加唯一字段的迁移
当数据表存在记录,又不能删除表的情况下,添加唯一字段会出现错误
class MyModel(models.Model): # ... uuid = models.UUIDField(default=uuid.uuid4, unique=True) # 新增字段
运行 makemigrations 命令,生成迁移文件0003_mymodel_uuid.py:
from __future__ import unicode_literals from django.db import migrations, models import uuid class Migration(migrations.Migration): dependencies = [ ('demo1', '0002_auto_20170818_1216'), ] operations = [ migrations.AddField( model_name='mymodel', name='uuid', field=models.UUIDField(default=uuid.uuid4, unique=True), ), ]
如果现在运行migrate命令会报错,我这里出现的错误是Duplicate KeyError
修改刚刚生成的迁移文件0003_mymodel_uuid.py:
- 从已生成的迁移类中复制,添加第二个AddField操作,并改为AlterField。
- 在第一个AddField操作中,把unique=True改为 null=True,这会创建一个中间的null字段。
- 在两个操作之间,添加一个RunPython或RunSQL操作为每个已存在的行生成一个唯一值(例如UUID)
def gen_uuid(apps, schema_editor): MyModel = apps.get_model('demo1', 'MyModel') for row in MyModel.objects.all(): row.uuid = uuid.uuid4() row.save() class Migration(migrations.Migration): dependencies = [ ('demo1', '0002_auto_20170818_1216'), ] # 1.先增加uuid字段,可以为空 operations = [ migrations.AddField( model_name='mymodel', name='uuid', field=models.UUIDField(default=uuid.uuid4, null=True), ), # omit reverse_code=... if you don't want the migration to be reversible. # 2.调用gen_uuid 为该字段所有记录赋值更新并保存到数据库 migrations.RunPython(gen_uuid, reverse_code=migrations.RunPython.noop), # 3.修改字段属性,增加unique=True migrations.AlterField( model_name='mymodel', name='uuid', field=models.UUIDField(default=uuid.uuid4, unique=True), ), ]
使用migrate命令应用迁移。
注意
如果你在这个迁移运行时让对象被创建,就会产生竞争条件(race condition)。在AddField之后, RunPython之前创建的对象会覆写他们原始的uuid。