Adding django admin permissions in a migration: Permission matching query does not exist
Asked Answered
J

5

20

I wanted to add some groups and assign permissions to them in a manually written migration but if I run it on a clean DB it creates permissions only after running all migrations.

I've found this ticket: https://code.djangoproject.com/ticket/23422 but I cannot comment there (it's possible I was banned after expressing some discontent with GeoDjango docs), so I'll share an improvement over the solution there below.

Janot answered 30/7, 2015 at 22:18 Comment(0)
J
8

Django <= 1.9

see another answer for Django 1.10+

It's enough to call create_permissions:

from django.contrib.auth.management import create_permissions

apps.models_module = True
create_permissions(apps, verbosity=0)
apps.models_module = None

The whole migration being something like this

# coding:utf-8
from django.db import migrations
from django.contrib.auth.models import Permission, Group
from django.contrib.auth.management import create_permissions
from django.contrib.contenttypes.models import ContentType
from django.conf import settings

MODERATORS_PERMISSIONS = ['change_modelname', ]


def add_permissions(apps, schema_editor):
    apps.models_module = True
    create_permissions(apps, verbosity=0)
    apps.models_module = None

    moderators_group = Group.objects.get_or_create(
        name=settings.MODERATORS_GROUP)[0]
    for codename in MODERATORS_PERMISSIONS:
        permission = Permission.objects.get(codename=codename)
        moderators_group.permissions.add(permission)


class Migration(migrations.Migration):

    dependencies = [
        ('contenttypes', '0002_remove_content_type_name'),
        ('thisappname', '0001_initial'),
    ]

    operations = [
        migrations.RunPython(add_permissions),
    ]
Janot answered 30/7, 2015 at 22:18 Comment(4)
This doesn't work for Django 1.10. See xuhcc's answer.Runesmith
"NameError: global name 'apps' is not defined" Where is apps defined?Floruit
It's the first function argument, see def add_permissions(apps, schema_editor):Janot
@user8383599 I've seen your suggestion, but I think it has to be a separate answer. I both don't want to add a code I cannot test at the moment and wouldn't like taking away points that you can earn.Janot
M
40

In django 1.10 the following code could be used:

from django.contrib.auth.management import create_permissions

def migrate_permissions(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
More answered 17/10, 2016 at 17:57 Comment(4)
In what file do i add this? Do i create a seperate migration for this?Theran
@OleHenrikSkogstrøm This is added into the migration file, look up the migration command RunPython which would look like this migrations.RunPython(migrate_permissions),Gunn
In Django 3.2.5+ add ("contenttypes", "0002_remove_content_type_name") to your dependencies. A migrations has been added (0002) removing the 'name' field. This migration is fairly low in the migration tree and therefore might not be applied yet when you create the permissions, resulting in django.db.utils.IntegrityError: null value in column "name" of relation "django_content_type" violates not-null constraint DETAIL: Failing row contains (1, null, admin, logentry)Bergquist
As a matter of fact.. just add ("auth", "0012_alter_user_first_name_max_length") (or which ever seems appropriate for your use case). This way you know for sure the Permissions and ContentType existsBergquist
J
8

Django <= 1.9

see another answer for Django 1.10+

It's enough to call create_permissions:

from django.contrib.auth.management import create_permissions

apps.models_module = True
create_permissions(apps, verbosity=0)
apps.models_module = None

The whole migration being something like this

# coding:utf-8
from django.db import migrations
from django.contrib.auth.models import Permission, Group
from django.contrib.auth.management import create_permissions
from django.contrib.contenttypes.models import ContentType
from django.conf import settings

MODERATORS_PERMISSIONS = ['change_modelname', ]


def add_permissions(apps, schema_editor):
    apps.models_module = True
    create_permissions(apps, verbosity=0)
    apps.models_module = None

    moderators_group = Group.objects.get_or_create(
        name=settings.MODERATORS_GROUP)[0]
    for codename in MODERATORS_PERMISSIONS:
        permission = Permission.objects.get(codename=codename)
        moderators_group.permissions.add(permission)


class Migration(migrations.Migration):

    dependencies = [
        ('contenttypes', '0002_remove_content_type_name'),
        ('thisappname', '0001_initial'),
    ]

    operations = [
        migrations.RunPython(add_permissions),
    ]
Janot answered 30/7, 2015 at 22:18 Comment(4)
This doesn't work for Django 1.10. See xuhcc's answer.Runesmith
"NameError: global name 'apps' is not defined" Where is apps defined?Floruit
It's the first function argument, see def add_permissions(apps, schema_editor):Janot
@user8383599 I've seen your suggestion, but I think it has to be a separate answer. I both don't want to add a code I cannot test at the moment and wouldn't like taking away points that you can earn.Janot
I
4

And if you want something that will work on any version (or that will keep working when you upgrade):

from django.contrib.auth.management import create_permissions

version = django.VERSION
if version[0] >= 1 and django.VERSION[1] > 9:
    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
else:
    apps.models_module = True
    create_permissions(apps, verbosity=0)
    apps.models_module = None
Isabelleisac answered 16/5, 2017 at 21:50 Comment(0)
M
0

Trying to get an permission during migrations causes an exception(Permission matching query does not exist) in Django. It's an old problem in Django. In 1.6 version I solved it via @int_ua's snippet but in 1.11 version it doesn't work(I'm not sure why).

I used this workaround in 1.11 version:

def _assign_group_permissions(permission_codenames, apps, group_name):
    permission_list = []
    Permission = apps.get_model('auth', 'Permission')

    for permission_codename in permission_codenames:
        for permission in Permission.objects.all():
            if permission.codename == permission_codename:
                permission_list.append(permission)

    Group = apps.get_model('auth', 'Group')
    group = Group.objects.get(name=group_name)
    group.permissions.add(*permission_list)

Instead of Permission.objects.get(codename='your_code_name') it's possible to iterate over all permissions and choose suitable one by codename.

Multilingual answered 25/7, 2018 at 18:31 Comment(0)
S
-3

Django 3.2

Here is a version for Django 3.2, which you can run from the command line:

./manage.py fix_permissions

# app_label/management/commands/fix_permissions.py

from django.contrib.auth.models import Permission
from django.contrib.auth.management import create_permissions
from django.core.management.base import BaseCommand
from django.apps import apps

class Command(BaseCommand):
    help = 'Recreate permissions from scratch'

    def handle(self, *args, **options):
        # Run this method via shell whenever any amendments in any of the tables is made

        print("Deleting existing user permissions...")
        Permission.objects.all().delete()

        for app_config in apps.get_app_configs():
            print(f"Adding user permissions for {app_config}...")
            app_config.models_module = True
            create_permissions(app_config, apps=apps, verbosity=0)
            app_config.models_module = None

        print("DONE.")


Sweetbread answered 22/2, 2021 at 12:59 Comment(2)
If one is running it from the command line after migrations have been run, the permissions should already be there and not need to be created or recreated.Shielashield
The question it about adding permissions in a migration. This "solution" adds it in a management command and thus does not answer the question.Plenipotent

© 2022 - 2024 — McMap. All rights reserved.