Programmatically create a django group with permissions
Inspired by radtek's answer I created a bit better version (in my opinion). It allows specifying model as object (instead of string) and specifying all configuration in one dictionary (instead of several lists)
# backend/management/commands/initgroups.py
from django.core.management import BaseCommand
from django.contrib.auth.models import Group, Permission
from backend import models
GROUPS_PERMISSIONS = {
'ConnectionAdmins': {
models.StaticCredentials: ['add', 'change', 'delete', 'view'],
models.NamedCredentials: ['add', 'change', 'delete', 'view'],
models.Folder: ['add', 'change', 'delete', 'view'],
models.AppSettings: ['view'],
},
}
class Command(BaseCommand):
def __init__(self, *args, **kwargs):
super(Command, self).__init__(*args, **kwargs)
help = "Create default groups"
def handle(self, *args, **options):
# Loop groups
for group_name in GROUPS_PERMISSIONS:
# Get or create group
group, created = Group.objects.get_or_create(name=group_name)
# Loop models in group
for model_cls in GROUPS_PERMISSIONS[group_name]:
# Loop permissions in group/model
for perm_index, perm_name in \
enumerate(GROUPS_PERMISSIONS[group_name][model_cls]):
# Generate permission name as Django would generate it
codename = perm_name + "_" + model_cls._meta.model_name
try:
# Find permission object and add to group
perm = Permission.objects.get(codename=codename)
group.permissions.add(perm)
self.stdout.write("Adding "
+ codename
+ " to group "
+ group.__str__())
except Permission.DoesNotExist:
self.stdout.write(codename + " not found")
I needed to create a default set of groups and permission (view only) for those groups. I came up with a manage.py command that may be useful to others (create_groups.py). You can add it to your <app>/management/commands
dir, and then run via manage.py create_groups
:
"""
Create permission groups
Create permissions (read only) to models for a set of groups
"""
import logging
from django.core.management.base import BaseCommand
from django.contrib.auth.models import Group
from django.contrib.auth.models import Permission
GROUPS = ['developers', 'devops', 'qa', 'operators', 'product']
MODELS = ['video', 'article', 'license', 'list', 'page', 'client']
PERMISSIONS = ['view', ] # For now only view permission by default for all, others include add, delete, change
class Command(BaseCommand):
help = 'Creates read only default permission groups for users'
def handle(self, *args, **options):
for group in GROUPS:
new_group, created = Group.objects.get_or_create(name=group)
for model in MODELS:
for permission in PERMISSIONS:
name = 'Can {} {}'.format(permission, model)
print("Creating {}".format(name))
try:
model_add_perm = Permission.objects.get(name=name)
except Permission.DoesNotExist:
logging.warning("Permission not found with name '{}'.".format(name))
continue
new_group.permissions.add(model_add_perm)
print("Created default group and permissions.")
UPDATE: A bit more sophisticated now:
from django.contrib.auth.models import Group
from django.contrib.auth.models import User
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
READ_PERMISSIONS = ['view', ] # For now only view permission by default for all, others include add, delete, change
WRITE_PERMISSIONS = ['add', 'change', 'delete']
EMAIL_USER_DOMAIN = 'your-domain.com'
# Add your groups here, app and model code
GROUP_MODEL = ('auth', 'group')
USER_MODEL = ('auth', 'user')
PERMISSION_MODEL = ('auth', 'permission')
LOG_ENTRY_MODEL = ('admin', 'logentry')
def add_group_permissions(group_names, model_natural_keys, permissions):
"""
Add permissions to the provided groups for the listed models.
Error raised if permission or `ContentType` can't be found.
:param group_names: iterable of group names
:param model_natural_keys: iterable of 2-tuples containing natural keys for ContentType
:param permissions: iterable of str (permission names i.e. add, view)
"""
for group_name in group_names:
group, created = Group.objects.get_or_create(name=group_name)
for model_natural_key in model_natural_keys:
perm_to_add = []
for permission in permissions:
# using the 2nd element of `model_natural_key` which is the
# model name to derive the permission `codename`
permission_codename = f"{permission}_{model_natural_key[1]}"
try:
perm_to_add.append(
Permission.objects.get_by_natural_key(
permission_codename, *model_natural_key
)
)
except Permission.DoesNotExist:
# trying to add a permission that doesn't exist; log and continue
logging.error(
f"permissions.add_group_permissions Permission not found with name {permission_codename!r}."
)
raise
except ContentType.DoesNotExist:
# trying to add a permission that doesn't exist; log and continue
logging.error(
"permissions.add_group_permissions ContentType not found with "
f"natural name {model_natural_key!r}."
)
raise
group.permissions.add(*perm_to_add)
def set_users_group(users, group):
"""
Adds users to specific permission group.
If user or group does not exist, they are created.
Intended for use with special users for api key auth.
:param users: list of str, usernames
:param group: str, group for which users should be added to
:return: list, user objects added to group
"""
users = users or []
user_objs = []
for user_name in users:
try:
user = User.objects.get(username=user_name)
except User.DoesNotExist:
user = User.objects.create_user(username=user_name,
email=f'{user_name}@{EMAIL_USER_DOMAIN}',
password='')
user_objs.append(user)
group, created = Group.objects.get_or_create(name=group)
group.user_set.add(user)
return user_objs
API_READ_GROUP = 'api-read-users'
API_WRITE_GROUP = 'api-write-users'
READ_GROUPS = [API_READ_GROUP, ]
WRITE_GROUPS = [API_WRITE_GROUP, ] # Can be used in same way as read users below
# Adding users to a group
set_users_group(READ_USERS, API_READ_GROUP)
# Setting up the group permissions i.e. read for a group of models
add_group_permissions(READ_GROUPS, [GROUP_MODEL, USER_MODEL, LOG_ENTRY_MODEL], READ_PERMISSIONS)
I also found that using manage.py update_permissions
is useful to sort out/clean up stale permissions if models have changed etc.. Its part of django-extensions commands.
Use below code
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
from api.models import Project
new_group, created = Group.objects.get_or_create(name='new_group')
# Code to add permission to group ???
ct = ContentType.objects.get_for_model(Project)
# Now what - Say I want to add 'Can add project' permission to new_group?
permission = Permission.objects.create(codename='can_add_project',
name='Can add project',
content_type=ct)
new_group.permissions.add(permission)