【问题标题】:Django 1.7 datamigration and user groupsDjango 1.7 数据迁移和用户组
【发布时间】:2014-11-25 22:46:15
【问题描述】:

我正在尝试使用 django 1.7 本机迁移系统实现数据迁移。这是我所做的。

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import migrations


def create_basic_user_group(apps, schema_editor):
    """Forward data migration that create the basic_user group

    """
    Group = apps.get_model('auth', 'Group')
    Permission = apps.get_model('auth', 'Permission')
    group = Group(name='basic_user')
    group.save()

    perm_codenames = (
        'add_stuff',
        '...',
    )

    # we prefere looping over all these in order to be sure to fetch them all
    perms = [Permission.objects.get(codename=codename)
             for codename in perm_codenames]

    group.permissions.add(*perms)
    group.save()


def remove_basic_user_group(apps, schema_editor):
    """Backward data migration that remove the basic_user group

    """
    group = Group.objects.get(name='basic_user')
    group.delete()


class Migration(migrations.Migration):
    """This migrations automatically create the basic_user group.

    """

    dependencies = [
    ]

    operations = [
        migrations.RunPython(create_basic_user_group, remove_basic_user_group),
    ]

但是当我尝试运行迁移时,我收到了一个 LookupError 异常,告诉我找不到标签为“auth”的应用程序。

我怎样才能以简洁的方式创建我的组,也可以在单元测试中使用?

【问题讨论】:

  • 尝试app.get_registered_model 和/或依赖('auth', 'group')。这是一种随机的建议,因为我自己仍在了解注册表的过程中。它帮助我解决了一个类似的问题。
  • 在 django 1.8 中,对象管理器可以在迁移期间使用。特别是现在您的代码应该按原样工作

标签: python django migration


【解决方案1】:

我已经完成了你想做的事情。问题是:

  1. 1.71.8 的文档非常清楚:如果您想从另一个应用程序访问模型,则必须将此应用程序列为依赖项:

    在编写RunPython 函数时,该函数使用迁移所在应用程序以外的应用程序的模型,迁移的依赖项属性应包括所涉及的每个应用程序的最新迁移,否则您可能会收到类似的错误: LookupError: No installed app with label 'myappname' 当您尝试使用apps.get_model()RunPython 函数中检索模型时。

    所以你应该依赖auth 中的最新迁移。

  2. 正如您在comment 中提到的那样,您将遇到一个尚未创建您要使用的权限的问题。问题是权限是由附加到post_migrate 信号的信号处理程序创建的。因此,在迁移完成之前,与迁移中创建的任何 new 模型相关的权限均不可用。

    您可以通过在create_basic_user_group 开头执行此操作来解决此问题:

    from django.contrib.contenttypes.management import update_contenttypes
    from django.apps import apps as configured_apps
    from django.contrib.auth.management import create_permissions
    
    for app in configured_apps.get_app_configs():
        update_contenttypes(app, interactive=True, verbosity=0)
    
    for app in configured_apps.get_app_configs():
        create_permissions(app, verbosity=0)
    

    这还将为每个模型创建内容类型(它们也是在迁移之后创建的),请参阅下文,了解您应该关心的原因。

    也许您可能比我在上面的代码中更有选择性:只更新一些关键应用程序而不是更新所有应用程序。我没有试图有选择性。此外,两个循环也有可能合并为一个。我没有用一个循环尝试过。

  3. 您可以通过codename 搜索获得您的Permission 对象,但codename 不能保证是唯一的。两个应用程序可以拥有名为Stuff 的模型,因此您可以拥有与两个不同应用程序关联的add_stuff 权限。如果发生这种情况,您的代码将失败。您应该做的是通过codenamecontent_type 进行搜索,它们保证在一起是唯一的。一个唯一的content_type 与项目中的每个模型相关联:两个具有相同名称但在不同应用中的模型将获得两种不同的内容类型。

    这意味着添加对contenttypes 应用程序的依赖,并使用ContentType 模型:ContentType = apps.get_model("contenttypes", "ContentType")

【讨论】:

    【解决方案2】:

    正如https://code.djangoproject.com/ticket/23422 中所说,信号post_migrate 应该在处理Permission 对象之前发送。

    但是 Django 上已经有一个辅助函数来发送所需的信号:django.core.management.sql.emit_post_migrate_signal

    在这里,它是这样工作的:

    # -*- coding: utf-8 -*-
    from __future__ import unicode_literals
    
    from django.db import models, migrations
    from django.core.management.sql import emit_post_migrate_signal
    
    
    PERMISSIONS_TO_ADD = [
        'view_my_stuff',
        ...
    ]
    
    
    def create_group(apps, schema_editor):
        # Workarounds a Django bug: https://code.djangoproject.com/ticket/23422
        db_alias = schema_editor.connection.alias
        try:
            emit_post_migrate_signal(2, False, 'default', db_alias)
        except TypeError:  # Django < 1.8
            emit_post_migrate_signal([], 2, False, 'default', db_alias)
    
        Group = apps.get_model('auth', 'Group')
        Permission = apps.get_model('auth', 'Permission')
    
        group, created = Group.objects.get_or_create(name='MyGroup')
        permissions = [Permission.objects.get(codename=i) for i in PERMISSIONS_TO_ADD]
        group.permissions.add(*permissions)
    
    
    class Migration(migrations.Migration):
    
        dependencies = [
            ('auth', '0001_initial'),
            ('myapp', '0002_mymigration'),
        ]
    
        operations = [
            migrations.RunPython(create_group),
        ]
    

    【讨论】:

      【解决方案3】:

      所以,我想出了如何解决这个问题并得到以下退出:get_model 只会获取您的模型应用程序。我不确定这是否是一个好的做法,但它对我有用。

      我只是直接调用模型并进行了更改。

      # -*- coding: utf-8 -*-
      from __future__ import unicode_literals
      from django.db import models, migrations
      from django.contrib.auth.models import Group
      
      
      def create_groups(apps, schema_editor):
          g = Group(name='My New Group')
          g.save()
      
      
      class Migration(migrations.Migration):
      
          operations = [
              migrations.RunPython(create_groups)
          ]
      

      然后,只需应用 /manage.py 迁移即可完成。 希望对你有帮助。

      【讨论】:

      • 这行得通,但您不能向它们添加权限,因为它们是在迁移后信号上创建的。事实上,如果您迁移已经存在的数据库,您会认为它可以工作,因为权限对象是在之前的迁移中创建的。但它会在针对新的空数据库的迁移时失败。
      猜你喜欢
      • 2014-12-13
      • 2015-08-20
      • 2014-11-15
      • 2015-10-31
      • 2014-05-28
      • 2015-02-09
      • 2014-12-21
      • 1970-01-01
      • 2015-05-06
      相关资源
      最近更新 更多