Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why I can't assign new permission to group in the same migration in Django

I'm trying to add new migrations by following this tutorial I added new permission inside Meta into permissions field. Then I created migration and tryed to modify this migration to update group permissions in place. But got DoesNotExist in RunPython operation.

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)
    ]
like image 903
Daniil Mashkin Avatar asked Dec 05 '22 12:12

Daniil Mashkin


1 Answers

I think that your problem occurs because permissions are not actually created during or after an individual migration, but are triggered by a post-migrate signal which is sent after the python manage.py migrate command completes successfully (see comments on the accepted answer here.)

There are a few ways around it:

  1. Split it into two separate migrations (create permissions and then assign them) and run them with two separate python manage.py migrate commands:

    python manage.py migrate my_app 0066_create_permissions
    python manage.py migrate my_app 0067_assign_permissions
    

    This allows the post-migrate signal to be emitted after 0066 is run to create the permissions.

  2. Split it up into two steps inside one migration, but you'll have to manually create the permissions with a Python function and then assign them in another. You'll have to modify your operations to account for that. One benefit of this is that if want to, you can create a Python function or functions to reverse the migration as well, which isn't really possible for #3 or #4.

  3. Emit the post-migrate signal yourself during the migration. This is a good solution for cases where you need permissions from 3rd party apps (like django-guardian, as in my case) so that you can apply them in data migrations.

    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,
        )
    
  4. This is similar to #3, but a little easier. There are probably subtle ways in which it is better or worse, but I'm not sure of what they are. You can create a function which calls django.contrib.auth.management.create_permissions (credit to this post) and use it directly in your migration:

    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
    

    Then your operations would look like:

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

Anyway, I hope that helps. Sorry for the information overload - I've had the same issue as you in the past and it's set me back a few days.

like image 50
t354 Avatar answered Dec 25 '22 23:12

t354