转载

Django开发Migration使用随笔

部分参考自: http://blog.csdn.net/kingmari/article/details/7908153

migration文件产生:

in-memery structure计算models和当前migration文件的整体状态计算出差异,生成新的migration文件。

from django.db import migrations, models class Migration(migrations.Migration): dependencies = [("migrations", "0001_initial")] operations = [     migrations.DeleteModel("Tribble"),     migrations.AddField("Author", "rating",      models.IntegerField(default=0)), ] 
  • dependencies :当前文件依赖的migrations文件列表。
  • operations :当前文件定义的数据库操作。

开发遇到的问题:

  • 已有database和model但是无migration文件:

    • 首先执行: $ python manage.py makemigrations your_app_label

    之后会产生当前app的一个新的初始migration文件。

    • 然后执行: $ python manage.py migrate --fake-initial

    Django 将会确认你已经有一个初始migration文件并且数据表已经存在,并且标记生成的migration文件已经执行过了。

    这能正常工作需要2个条件:

    • 生成表之后没有变更过模型。为了迁移能工作,需要先做初始化迁移工作,然后再变更模型,因为django和迁移脚本做变更对比,而不是数据库。
    • 没有手动修改过数据库 - django不能检测到你的数据库和模型的不匹配,当使用迁移修改这些表时只能得到error。
  • 多人开发数据冲突问题:

    • 两个人使用同一个migration文件目录,生成的migration文件可能存在冲突:

    这种情况下Django会提醒你并给出相应操作,如果Django认为无影响,会依次执行执行migration里的修改,否则需要修改migration文件。

    个人意见:

    最好时刻保持database和model一致,开发者之间常沟通,下面提到的数据迁移最好用导出sql脚本的方式,数据迁移写着麻烦,也容易出错。

  • 数据迁移:

    变更数据的迁移被称为“data migrations”;它们最好写成单独的脚本,放在你的架构迁移脚本旁边。

    django不会自动为你创建数据迁移脚本,它只会生成架构迁移,但是写这些数据迁移也不是很难。django中的迁移脚本由 Operations组成,数据迁移的主要操作是RunPython。

    开始时,生成一个空的迁移脚本(django会放置文件在正确的地方,建议一个文件名称,并添加依赖):

    $ python manage.py makemigrations --empty yourappname

    然后,打开文文件:

    from django.db import models, migrations   class Migration(migrations.Migration):     dependencies = [         ('yourappname', '0001_initial'),     ]     operations = [     ] 

    现在,你所需要做的就是创建一个新的函数让RunPython调用。

    文档示例:

    from django.db import models, migrations def combine_names(apps, schema_editor):  Person = apps.get_model("yourappname", "Person")  for person in Person.objects.all():   person.name = "%s %s" % (person.first_name, person.last_name)   person.save() class Migration(migrations.Migration):  dependencies = [   ('yourappname', '0001_initial'),  ]  operations = [   migrations.RunPython(combine_names),  ]  

    一旦完成了脚本,我们能正常运行python manage.py migrate,数据迁移会和其他迁移一同完成。

  • 合并迁移

    Django通过收集所有现存的迁移脚本,抽出它们的操作,然后把它们放到一个序列中,对它们进行一个优化来减少序列的长度 - 例如,它知道CreateModel 和 DeleteModel 操作相互抵消,它也知道AddField可以整合到CreateModel中。

    一旦操作序列被尽可能的减少 - 这个数量可能取决于你的模型相互缠结的程度和是否有RunSQL 或 RunPython 操作 (这些都不能被优化) - Django将把它们写回到一套新的初始化迁移脚本中。

    这些脚本被标记为取代之前的脚本 - 合并脚本,所以它们能够和旧的脚本共存,Django能智能的在它们之中切换。如果你还处于合并迁移过程中,它会继续使用原来的脚本直到合并脚本的最后一项,然后切换到合并历史版本,这样新安装时会只使用新的合并脚本而跳过所有老的脚本。

    这保证了你能进行合并而不会弄乱并非完全保持最新的生产系统。推荐流程是进行合并,保留老的脚本,提交和发布,等待所有系统都升级到新的版本(或者如果是第三方工程,保证你的用户顺序升级而不要跳过任何一步),然后删除老的文件,提交和进行下个版本开发。

    命令是squashmigrations:

    $ ./manage.py squashmigrations myapp 0004

    注意模型依赖在Django中可能会非常复杂,合并可能导致优化后的迁移不能功能或不能运行。这种情况下,你可以使用--no-optimize,不过请报告一个bug( file a bug report),告知模型细节和它们的关系,这样我们可以针对它改善优化器。

    一旦你合并了你的迁移,你应该和原迁移一起提交并发布变更到所有的你的运行中的应用上,确保它们运行migrate并将变更保存到数据库中。

    完成之后,你需要将合并迁移脚本转换成一个普通的初始化迁移脚本,通过:

    • 所有被代替的迁移脚本;
    • 并迁移脚本中的Migration类中删除replaces参数 (这是Django如何获知他是合并脚本的方式)。

    注意:

    一旦你合并了迁移,你不能重复合并直到你把它完全转换成一个普通的脚本。

  • 遗留数据库整合

    Django的数据库层从Python代码生成SQL schemas—但是对于遗留数据库,你已经拥有SQL schemas。这种情况,你需要为已经存在的数据表创建model。为此,Django自带了一个可以通过读取您的数据表结构来生成model的工具。该辅助工具称为inspectdb,你可以通过执行manage.py inspectdb来调用它。

    这种操作很渣,你需要对产生的模型代码做一些清理:

    • 数据库的每一个表都会被转化为一个model类 (也就是说,数据库的表和model 的类之间做一对一的映射)。 这意味着你需要为多对多连接的表,重构其models 为 ManyToManyField 的对象。

    • 每一个字段类型,如CharField、DateField, 是通过查找数据库列类型如VARCHAR,DATE来确定的。 如果inspectdb无法对某个model字段类型根据数据库列类型进行映射,那么它会使用TextField字段进行代替,并且会在所生成model 字段后面加入Python注释“该字段类型是猜的”。

    • 数据库中某个列的名字是Python的保留字,比如pass、class或者for等,inspectdb会在每个属性名后附加上 field,并将db column属性设置为真实的字段名,比如pass,class或者for等。

    • 库中某张表引用了其他表(正如大多数数据库系统所做的那样),你需要适当的修改所生成model的顺序,以使得这种引用能够正确映射。 例如,model Book拥有一个针对于model Author的外键,那么后者应该先于前者被定义。

    • PostgreSQL,MySQL和SQLite数据库系统,inspectdb能够自动检测出主键关系。 也就是说,它会在合适的位置插入primary key=True。 而对于其他数据库系统,你必须为每一个model中至少一个字段插入这样的语句,因为Django的model要求必须拥有一个 primary key=True的字段。

    • 检测仅对PostgreSQL,还有MySQL表中的某些特定类型生效。 至于其他数据库,外键字段都将在假定其为INT列的情况下被自动生成为IntegerField。

正文到此结束
Loading...