17

I have been trying to find the answer in the Django Auth docs, but can not seem to find what I am looking for.

The problem I am having is, when I define the code for adding Groups (same as Groups in the admin page):

#read_only
group, created = Group.objects.get_or_create(name='read_only')   
if created:
    group.permissions.add(can_read_campaign)
    logger.info('read_only_user Group created')
#standard
group, created = Group.objects.get_or_create(name='standard_user') 
if created:
    group.permissions.add(can_edit_users)
    logger.info('standard_user Group created')
#admin
group, created = Group.objects.get_or_create(name='admin_user') 
if created:
    group.permissions.add(can_edit_campaign, can_edit_users)
    logger.info('admin_user Group created')

When I have run this code in models.py and init.py and they both give me this error:

django.core.exceptions.AppRegistryNotReady

I presume this is due to the Model/init trying to insert things into the django app/admin too early?

How can I add these Groups programmatically?

EDIT:

This is not a duplicate question, this was actually adding permission and groups within the models during setup of the project, rather than through the shell.

I have solved this issues, by using signals and receivers (django modules).

I added the code to create the permissions/groups into it's own function and decorated this with a receiver (post_migrate), which will run this function after migrations are complete, removing this error.

@receiver(post_migrate)
def init_groups(sender, **kwargs):
    #permission and group code goes here
Cœur
  • 37,241
  • 25
  • 195
  • 267
adds68
  • 301
  • 2
  • 11
  • You need to call the [`setup()`](https://docs.djangoproject.com/en/dev/ref/applications/#application-registry) function. I would close this as a duplicate - let us know if it is really more than that. – alecxe Jul 29 '14 at 20:54
  • Hi @alecxe this was not a duplicate question, i have provided an edit for you. – adds68 Aug 28 '14 at 22:04

3 Answers3

30

I was recommended this way to do it:

Create a fake migration in the appropriate module:

python manage.py makemigrations --empty yourappname

Open up the file that was created, which should look like this:

# -*- coding: utf-8 -*-
from django.db import models, migrations

class Migration(migrations.Migration):

    dependencies = [
        ('yourappname', '0001_initial'),
    ]

    operations = [
    ]

And add your code:

# -*- coding: utf-8 -*-
from django.db import models, migrations

def add_group_permissions():
    #read_only
    group, created = Group.objects.get_or_create(name='read_only')   
    if created:
        group.permissions.add(can_read_campaign)
        logger.info('read_only_user Group created')

    #standard
    group, created = Group.objects.get_or_create(name='standard_user') 
    if created:
        group.permissions.add(can_edit_users)
        logger.info('standard_user Group created')

    #admin
    group, created = Group.objects.get_or_create(name='admin_user') 
    if created:
        group.permissions.add(can_edit_campaign, can_edit_users)
        logger.info('admin_user Group created')

class Migration(migrations.Migration):

    dependencies = [
        ('yourappname', '0001_initial'),
    ]

    operations = [
        migrations.RunPython(add_group_permissions),
    ]

Finally, run the migration:

python manage.py migrate

This is nice because you can deploy to Heroku or wherever and be sure it'll be applied, as it's just another migration.

Rob Grant
  • 7,239
  • 4
  • 41
  • 61
  • 1
    Nice answer, very neat way to do it. – adds68 Sep 16 '14 at 12:40
  • 1
    Just an improvement: def create_group(name, perms=[]): group, created = Group.objects.get_or_create(name=name) if created: for perm in perms: group.permissions.add(perm) logger.info("Group \"" + name + "\" created") – Filip Dobrovolný Jun 18 '15 at 16:16
  • 14
    In Django 1.8 I had to add two things to this answer: `from django.contrib.auth.models import Group` at the top or it complains about Group not being defined. and then the function definition needed two params: `def add_group_permissions(apps, schema_editor):`. – Banjer Nov 08 '15 at 02:36
  • 1
    In django 1.9.5, trying to fetch permissions during migrations causes an error for me http://stackoverflow.com/questions/38491215/programmatically-creating-a-group-cant-access-permissions-from-migration/38491679#38491679 . – vmonteco Jul 21 '16 at 00:29
5

Combining @Robert Grant and this I was able to do it like:

python manage.py makemigrations --empty yourappname

And then:

from django.contrib.auth.models import Group, Permission
from django.db import models, migrations
import logging


logger = logging.getLogger(__name__)

campaign_group_permissions = {
  "Campaign Manager": [
    "add_campaign",
    "change_campaign",
    "delete_campaign",
    "view_campaign",
    "add_campaignsms",
    "add_sending",
    "change_sending",
    "view_sending"
  ]
}


def add_group_permissions():
    # See https://code.djangoproject.com/ticket/23422
    db_alias = schema_editor.connection.alias
    try:
        emit_post_migrate_signal(2, False, 'default')
    except TypeError:  # Django < 1.8
        emit_post_migrate_signal([], 2, False, 'default', db_alias)

    for group in campaign_group_permissions:
        role, created = Group.objects.get_or_create(name=group)
        logger.info(f'{group} Group created')
        for perm in campaign_group_permissions[group]:
            role.permissions.add(Permission.objects.get(codename=perm))
            logger.info(f'Permitting {group} to {perm}')
        role.save()


class Migration(migrations.Migration):

    dependencies = [
        ('yourappname', '0001_initial'),
    ]

    operations = [
        migrations.RunPython(add_group_permissions),
    ]

Note: this works on Django 3.x, but I'm pretty sure it will work for Django 1.7 as well.

ruloweb
  • 704
  • 8
  • 10
5

@Ruloweb's fantastic response almost worked for me, but I had to make a couple tweaks to get it to work in Django 3.1 with multiple apps.

First, I needed to add arguments to the add_group_permissions() function. I also needed to import the emit_post_migration_signal:

from django.contrib.auth.models import Group, Permission
from django.core.management.sql import emit_post_migrate_signal # <-- Added this
from django.db import models, migrations
import logging

logger = logging.getLogger(__name__)

public_group_permissions = {
  "Your permission group name here": ['your permissions here']
}

def add_group_permissions(apps, schema_editor): # <-- updated this

    # See https://code.djangoproject.com/ticket/23422
    db_alias = schema_editor.connection.alias

    try:
        emit_post_migrate_signal(2, False, 'default')
    except TypeError:  # Django < 1.8
        emit_post_migrate_signal([], 2, False, 'default', db_alias)

    for group in public_group_permissions:
        role, created = Group.objects.get_or_create(name=group)
        logger.info(f'{group} Group created')
        for perm in public_group_permissions[group]:
            role.permissions.add(Permission.objects.get(codename=perm))
            logger.info(f'Permitting {group} to {perm}')
        role.save()


class Migration(migrations.Migration):

    dependencies = [
        ('your_app_name', '0001_initial'),
    ]

    operations = [
        migrations.RunPython(add_group_permissions),
    ]
JSv4
  • 193
  • 1
  • 8