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)
]
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:
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.
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.
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,
)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With