You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django同一迁移中为组分配新权限时出现DoesNotExist错误

解决Django迁移中RunPython触发Permission.DoesNotExist的问题

嘿,这个坑我之前踩过!你遇到的__fake__.DoesNotExist错误,核心原因是Django的模型权限并不是在你的迁移步骤里直接创建的——它是在所有迁移执行完成后,通过post_migrate信号来生成的。所以当你在同一个迁移文件的RunPython操作里去获取刚添加的权限时,这个权限还没被创建出来呢!

下面给你两个靠谱的解决方案:

方案一:拆分迁移文件(推荐)

最稳妥的方式是把「添加权限」和「分配权限给组」分成两个独立的迁移:

  1. 先正常生成添加权限的迁移:
    python manage.py makemigrations
    
    这会生成一个包含AlterModelOptions的迁移文件,用来更新模型的权限配置。
  2. 生成一个空的迁移文件专门处理权限分配:
    python manage.py makemigrations --empty your_app_name
    
  3. 在这个空迁移里编写你的权限分配逻辑:
    from django.db import migrations
    
    def assign_new_permission(apps, schema_editor):
        Permission = apps.get_model('auth.Permission')
        Group = apps.get_model('auth.Group')
        
        # 现在可以安全获取权限了,因为之前的迁移已经执行完,post_migrate已经创建了权限
        new_permission = Permission.objects.get(
            codename='your_new_permission_codename',
            content_type__app_label='your_app_name',
            content_type__model='your_model_name'
        )
        
        # 找到目标组并添加权限
        target_group = Group.objects.get(name='Your Target Group')
        target_group.permissions.add(new_permission)
    
    class Migration(migrations.Migration):
        dependencies = [
            # 依赖刚才生成的添加权限的迁移
            ('your_app_name', '000X_add_new_permission'),
        ]
    
        operations = [
            migrations.RunPython(assign_new_permission),
        ]
    
  4. 最后执行迁移:
    python manage.py migrate
    

方案二:在同一个迁移中手动创建权限

如果你一定要把逻辑放在同一个迁移里,那可以手动在RunPython之前创建权限,不依赖post_migrate信号:

from django.db import migrations

def create_new_permission(apps, schema_editor):
    Permission = apps.get_model('auth.Permission')
    ContentType = apps.get_model('contenttypes.ContentType')
    
    # 获取对应模型的ContentType
    content_type = ContentType.objects.get(
        app_label='your_app_name',
        model='your_model_name'
    )
    
    # 用get_or_create避免重复创建
    new_permission, _ = Permission.objects.get_or_create(
        codename='your_new_permission_codename',
        name='Your New Permission Label',
        content_type=content_type,
    )

def assign_new_permission(apps, schema_editor):
    Permission = apps.get_model('auth.Permission')
    Group = apps.get_model('auth.Group')
    
    new_permission = Permission.objects.get(
        codename='your_new_permission_codename',
        content_type__app_label='your_app_name',
        content_type__model='your_model_name'
    )
    
    target_group = Group.objects.get(name='Your Target Group')
    target_group.permissions.add(new_permission)

class Migration(migrations.Migration):
    dependencies = [
        ('your_app_name', '000X_previous_migration'),
    ]

    operations = [
        # 先手动创建权限
        migrations.RunPython(create_new_permission),
        # 再分配给组
        migrations.RunPython(assign_new_permission),
        # 保留自动生成的AlterModelOptions步骤
        migrations.AlterModelOptions(
            name='your_model_name',
            options={
                'permissions': [
                    # 这里是你新增的权限
                    ('your_new_permission_codename', 'Your New Permission Label'),
                    # 其他原有权限...
                ],
            },
        ),
    ]

关键注意事项

  • 永远用codename + content_type的组合来查询权限,避免不同模型的同名权限冲突。
  • 用get_or_create而不是get来创建权限,防止迁移重复执行时出错。
  • 方案一更符合Django的迁移设计规范,推荐优先使用。

内容的提问来源于stack exchange,提问作者Danil

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:02:49