Django迁移——包含多个开发分支的工作流



我很好奇其他django开发人员是如何通过迁移管理多个代码分支(例如在git中)的。

我的问题如下:我们在git中有多个特性分支,其中一些是django迁移的(其中一些是修改字段,或者完全删除字段)-当我切换分支(与git checkout some_other_branch)数据库并不总是反映新的代码,所以我遇到"随机"错误,其中一个db表列不存在了,等等…

现在,我只是删除数据库并重新创建它,但这意味着我必须重新创建一堆虚拟数据来重新启动工作。我可以使用fixture,但是它需要跟踪哪些数据去了哪里,这有点麻烦。

是否有一个好的/干净的方法来处理这个用例?我认为post-checkout git钩子脚本可以运行必要的迁移,但我甚至不知道是否迁移回滚是可能的。

迁移回滚是可能的,通常由django自动处理。

考虑以下模型:

class MyModel(models.Model):
    pass
    

如果执行python manage.py makemigrations myapp,将生成初始迁移脚本。然后可以运行python manage.py migrate myapp 0001来应用这个初始迁移。

如果之后你添加一个字段到你的模型:

class MyModel(models.Model):    
    my_field = models.CharField()
    

然后重新生成一个新的迁移,并应用它,您仍然可以回到初始状态。你就跑python manage.py migrate myapp 0001和ORM将向后移动,删除新字段。

处理数据迁移时更棘手,因为必须编写向前和向后代码。考虑通过python manage.py makemigrations myapp --empty创建的空迁移,你会得到这样的结果:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
def forward(apps, schema_editor):
    # load some data
    MyModel = apps.get_model('myapp', 'MyModel')
    
    while condition:
        instance = MyModel()
        instance.save()
    
def backward(apps, schema_editor):
    # delete previously loaded data
    MyModel = apps.get_model('myapp', 'MyModel')
    
    while condition:
        instance = MyModel.objects.get(myargs)
        instance.delete()
class Migration(migrations.Migration):
    dependencies = [
        ('myapp', '0003_auto_20150918_1153'),
    ]
    operations = [ 
        migrations.RunPython(forward, backward),
    ]
    

对于纯数据加载迁移,通常不需要向后迁移。但是,当您更改模式并更新现有行的时候(就像将列中的所有值转换为段符一样),您通常必须编写反向步骤。

在我们的团队中,我们尽量避免在同一时间处理相同的模型,以避免冲突。如果不可能,并且创建了两个具有相同编号(例如0002)的迁移,您仍然可以重命名其中的一个以更改应用它们的顺序(还记得更新)将迁移类的dependencies属性设置为您的新订单)。

如果你最终在不同的特征中同时处理相同的模型字段,你仍然会遇到麻烦,但这可能意味着这些特性是相关的,应该加以处理在一个分支中。

对于git-hooks部分,可能可以写一些东西,假设您在分支mybranch上并想检查另一个功能分支myfeature:

  1. 在切换之前,将当前应用的迁移列表转储到临时文件mybranch_database_state.txt
  2. 然后,应用myfeature分支迁移,如果有的话然后,当检查回mybranch时,您重新应用先前的数据库状态查看转储文件

然而,这对我来说似乎有点粗俗,并且可能很难正确处理所有场景:重基、合并、挑选等

在迁移冲突发生时处理它们对我来说似乎更容易。

我没有一个好的解决方案,但我感到痛苦。

结帐后的钩子太迟了。如果您在分支A上,并且签出分支B,并且B的迁移比A少,则回滚信息仅在A中,并且需要在签出之前运行

我在几个提交之间跳转试图找到一个bug的起源时遇到了这个问题。我们的数据库(甚至在开发阶段)是巨大的,所以删除和重新创建是不现实的。

我正在想象一个用于git-checkout的包装器:

  1. 注意您的INSTALLED_APPS的最新迁移
  2. 查看所请求的分支并在那里记录最新的迁移
  3. 对于#1中的迁移比#2中的迁移更远的每个应用,迁移回#2中最高的迁移
  4. 查看新的分支
  5. 对于每个在#2中迁移领先于#1的应用程序,向前迁移

一个简单的编程问题!

到目前为止,我已经找到了两个Github项目(django-south-compassdjango_nomad),试图解决开发分支之间迁移的问题,Stack Overflow上有几个答案。

引用Medium上的一篇文章,大多数解决方案可以归结为以下概念之一:

    删除所有的表,并从头开始在目标分支中重新应用迁移。当从头创建表时,所有数据都将丢失,并且还需要重新创建。这可以通过fixture和数据迁移来处理,但反过来管理它们将成为一场噩梦,更不用说它将花费一些时间(…)
  1. 为每个分支创建一个单独的数据库,并在每次使用sed等工具切换分支时使用目标分支的设置更改设置文件。这可以用post_checkout钩子来完成。为每个分支维护一个大型数据库将非常占用存储资源。此外,检出单个提交id可能会产生相同的错误。
  2. 查找源分支和目标分支之间迁移的差异,并应用差异。我们可以这样做与post_checkout脚本,但有一个小问题。这篇文章详细解释了这个问题。总而言之,post_checkout是在签出目标分支中的所有文件(包括迁移文件)之后运行的。当我们运行python manage.py migrate app1时,如果目标分支不包含源分支中的所有迁移,Django将无法找到应用反向迁移所需的缺失迁移。我们必须暂时签出源分支中的迁移文件,运行python manage.py migrate并签出目标分支中的迁移文件。django-south-compass做了非常类似的事情,但只适用于python 2.6。
  3. 使用管理命令(使用python git模块),查找源分支和源分支和目标分支的merge-base之间的所有迁移操作差异,并通知用户这些更改。如果这些更改不影响分支更改的原因,则用户可以继续更改分支。否则,使用另一个管理命令,取消应用所有迁移,直到合并基础,切换分支,并在目标分支中应用迁移。会有小的数据丢失,如果两个分支没有很大的分歧,是可以管理的。django_nomad会做一些这样的工作。
  4. 在文件中跟踪已应用和未应用的迁移,并在切换分支时使用此数据填充表。

对于简单的更改,我依赖于迁移回滚,正如Agate所讨论的。

然而,如果我知道一个特性分支将涉及高度侵入性的数据库更改,或者如果它将涉及大量数据迁移,我喜欢在启动新分支时立即创建本地(或远程开发)数据库的克隆。这可能并不总是很方便,但特别是对于使用sqlite的本地开发,它只是复制一个文件(不受源代码控制)的问题。

在新分支上的第一次提交会更新我的Django设置(local/dev)以使用克隆的数据库。这样,当我切换分支时,就会自动选择正确的数据库。无需担心回滚模式更改、丢失数据等问题。没有复杂的东西。

特性分支完全合并后,克隆数据库可以被移除

最新更新