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.

int_ua
  • 1,646
  • 2
  • 18
  • 32

5 Answers5

38

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
xuhcc
  • 2,400
  • 24
  • 23
  • 7
    In what file do i add this? Do i create a seperate migration for this? – Ole Henrik Skogstrøm Dec 13 '16 at 20:32
  • 1
    @OleHenrikSkogstrøm This is added into the migration file, look up the migration command `RunPython` which would look like this `migrations.RunPython(migrate_permissions),` – oden Apr 26 '20 at 23:54
  • 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)` – Wouter Klein Heerenbrink Jul 23 '21 at 10:55
  • 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 exists – Wouter Klein Heerenbrink Jul 23 '21 at 14:40
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),
    ]
int_ua
  • 1,646
  • 2
  • 18
  • 32
  • 1
    This doesn't work for Django 1.10. See xuhcc's answer. – TimH - Codidact Dec 06 '16 at 21:44
  • "NameError: global name 'apps' is not defined" Where is `apps` defined? – Cerin Jul 26 '17 at 19:32
  • It's the first function argument, see `def add_permissions(apps, schema_editor):` – int_ua Jul 27 '17 at 23:06
  • @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. – int_ua Sep 24 '17 at 14:59
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
mlissner
  • 17,359
  • 18
  • 106
  • 169
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.

Greg Eremeev
  • 1,760
  • 5
  • 23
  • 33
-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.")


caram
  • 1,494
  • 13
  • 21
  • 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. – Scott Cranfill Oct 06 '21 at 16:48
  • 2
    The question it about adding permissions in a migration. This "solution" adds it in a management command and thus does not answer the question. – Michael Herrmann Dec 14 '21 at 10:59