为什么我无法在Django中的同一迁移中为组分配新权限

Dan*_*kin 1 django django-models django-permissions django-migrations

我试图按照此添加新的迁移教程 我增加了新的权限内Metapermissions现场。然后,我创建了迁移,并尝试修改此迁移以适当地更新组权限。但得到DoesNotExistRunPython操作。

from django.db import migrations


def assign_new_permission(apps, *args):
    Permission = apps.get_model('auth.Permission')
    Group = apps.get_model('auth.Group')
    # __fake__.DoesNotExist: Permission matching query does not exist.
    new_permission = Permission.objects.get(
        codename='my_new_permissoin_code')

    admins = Group.objects.get(name='Group name')
    admins.permissions.add(new_permission)


class Migration(migrations.Migration):
    dependencies = [
        ('my_app', '0066_some_migratoin'),
    ]

    operations = [
        migrations.AlterModelOptions(
            name='my_model',
            options={'permissions': (('my_new_permissoin_code',
                                      'Permission name'),)},
        ),
        migrations.RunPython(assign_new_permission)
    ]
Run Code Online (Sandbox Code Playgroud)

t35*_*354 5

我认为您的问题之所以出现,是因为权限实际上不是在单个迁​​移期间或之后创建的,而是由python manage.py migrate命令成功完成后发送的迁移后信号触发的(请参见此处有关已接受答案的注释)。

有几种解决方法:

  1. 将其分为两个单独的迁移(创建权限,然后分配它们),并使用两个单独的python manage.py migrate命令运行它们:

    python manage.py migrate my_app 0066_create_permissions
    python manage.py migrate my_app 0067_assign_permissions
    
    Run Code Online (Sandbox Code Playgroud)

    这允许运行0066创建权限后发出迁移后信号。

  2. 在一次迁移中将其分为两个步骤,但是您必须使用Python函数手动创建权限,然后在另一个函数中分配权限。您必须修改您operations的帐户。这样的好处之一是,如果愿意,您还可以创建一个或多个Python函数来逆向迁移,这对于#3或#4而言实际上是不可能的。

  3. 在迁移期间自己发出迁移后的信号。对于需要第三方应用程序(例如django-guardian)的权限的情况,这是一个很好的解决方案,以便您可以将其应用于数据迁移。

    from django.apps import apps as django_apps
    def guardian_post_migrate_signal(apps, schema_editor):
        guardian_config = django_apps.get_app_config('guardian')
        models.signals.post_migrate.send(
            sender=guardian_config,
            app_config=guardian_config,
            verbosity=2,
            interactive=False,
            using=schema_editor.connection.alias,
        )
    
    Run Code Online (Sandbox Code Playgroud)
  4. 这类似于#3,但要容易一些。可能有好还是坏的微妙方法,但是我不确定它们是什么。您可以创建一个调用django.contrib.auth.management.create_permissions(记入此帖子)并直接在迁移中使用的函数:

    from django.contrib.auth.management import create_permissions
    
    def create_perms(apps, schema_editor):
        for app_config in apps.get_app_configs():
            app_config.models_module = True
            create_permissions(app_config, apps=apps, verbosity=0)
            app_config.models_module = None
    
    Run Code Online (Sandbox Code Playgroud)

    然后您operations将看起来像:

        operations = [
            migrations.AlterModelOptions(
                name='my_model',
                options={'permissions': (('my_new_permission_code', 'Permission name'),)},
            ),
            migrations.RunPython(create_perms),
            migrations.RunPython(assign_perms)
        ]
    
    Run Code Online (Sandbox Code Playgroud)

无论如何,我希望能有所帮助。抱歉,信息过多-过去我遇到过与您相同的问题,这让我退缩了几天。