【问题标题】:Django migration file in an other app?其他应用程序中的 Django 迁移文件?
【发布时间】:2015-06-17 00:29:58
【问题描述】:

让我们想象一个简化的 Django 项目:

<root>/lib/python2.7/site-packages/externalapp/shop
<root>/myapp

myapp 还通过添加一些字段来扩展externalapp.shop.models 模型。 manage.py makemigrations 确实生成了名为 0004_auto_20150410_2001.py 的架构迁移文件:

from __future__ import unicode_literals
from django.db import models, migrations


class Migration(migrations.Migration):

    # __init__ is added by me as an attempt how to tell django's
    # migration loader operations are for the different application
    def __init__(self, name, app_label):
        super(Migration, self).__init__(name, 'shop')

    dependencies = [
        ('myapp', '__first__'),
        ('shop', '0003_auto_20150408_0958'),
    ]

    operations = [
        migrations.AddField(
            model_name='product',
            name='vat',
            field=models.ForeignKey(to='myapp.VAT', null=True),
        ),
    ]

如果上述迁移架构默认放在&lt;root&gt;/lib/python2.7/site-packages/externalapp/shop/migrations/路径下,manage.py migrate成功,表字段添加正确。

但是,如果我确实将上述迁移文件移动到 myapp/migrations/manage.py migrate 之后会失败

django.core.management.base.CommandError:检测到迁移冲突(myapp 中的 0001_initial、0004_auto_20150410_2001)。 要修复它们,请运行“python manage.py makemigrations --merge”

错误消息我不太明白,建议 makemigrations --merge 失败并符合预期:

ValueError: 找不到集合的共同祖先([u'0001_initial', u'0004_auto_20150410_2001'])

我尝试覆盖 migrations.Migration.__init__ 以更改派生的 app_label 但似乎迁移加载程序忽略了它。

如何调整迁移文件使其可以在其他应用程序中运行? 原因是在生产中externalapp 源不能直接接触,是只读的。

【问题讨论】:

  • 如何将字段从自己的应用程序添加到外部应用程序?这听起来像是一场潜在的灾难。至于迁移,单个应用程序中的所有迁移必须形成从第一个迁移到最后一个迁移的单一、无分支的路径。您有两个用于迁移的端点。您应该将 ('myapp', '__first__') 依赖项更改为依赖于 myapp 中的最后一次迁移。
  • @knbk 感谢您的提示。它修复了冲突的迁移错误,但新增了 state.models[app_label, self.model_name_lower].fields.append((self.name, field)) KeyError: (u'myapp', u'product') i> 出现。对于这个特定的迁移,如何明确告诉 django/south 它应该在 externalapp.shop 而不是 myapp 上运行? 添加来自其他应用程序的一些字段,如果您知道自己在做什么,不必是一场灾难,甚至是更简单、更快的解决方案(无需额外的表和 sql 连接)。 Related blog 关于。
  • 更新:我确实删除了数据库,取消注释迁移初始化程序并重新运行所有迁移,它开始工作!谢谢。

标签: python django django-models django-admin


【解决方案1】:

要在 Django 项目中移动迁移文件,就像注入其他应用程序的模型一样,您需要确保在您的 django.db.migrations.Migration 后代中:

  • 显式设置应用程序名称,因为迁移加载程序会由迁移文件所在的应用程序自动派生它,否则会尝试在不同模型上执行操作
  • 通知迁移记录器它为其他应用程序提供迁移,否则它仍会将迁移视为未应用(有关已应用迁移的记录存储在一个表中,当前名为 django_migrations

我已经解决了迁移初始化程序中的问题,可能如下所示:

from django.db import migrations

TARGET_APP = 'shop'    # application label migration is for

class Migration(migrations.Migration):

    def __init__(self, name, app_label):
        # overriding application operated upon
        super(Migration, self).__init__(name, TARGET_APP)

    # specify what original migration file it replaces
    # or leave migration loader confused about unapplied migration
    replaces = ((TARGET_APP, __module__.rsplit('.', 1)[-1]),)

它确实对我有用,并且发现它足够通用。

如果可能的话,渴望听到更好/更简单的解决方案。

【讨论】:

  • 能否解释一下这一行:replaces = ((TARGET_APP, module..rsplit('.', 1)[-1]),)
  • @Mikael replaces 是一个类变量,它以(&lt;original migration&gt;, &lt;new migration&gt;) 的格式指定元组对,即。哪个迁移替代了原始迁移。 __module__ 指定定义迁移类后代的(点分隔)模块路径。 rsplit('.', 1)[-1] 提取路径的尾部。
  • 啊,令人困惑的部分是 module ,现在我明白了。谢谢!
【解决方案2】:

从 Django 1.9 开始,您可以使用 MIGRATION_MODULES 设置将“外来”模型迁移到您的应用中。

FeinCMS docs 中所述,您在应用中创建一个新包(带有__init__.py 的文件夹)并在设置中列出外国应用,如下所示:

MIGRATION_MODULES = {
    'one': 'yourapp.foreigners.one',
    'other': 'yourapp.foreigners.other',
}

之后你可以manage.py makemigrations one other 等。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-10-16
    • 2016-04-14
    • 2020-03-21
    • 1970-01-01
    • 2015-08-19
    • 1970-01-01
    • 2017-12-31
    • 2020-07-03
    相关资源
    最近更新 更多