Permissions & Groups

Difficulty: Advanced

Django's permission system provides fine-grained access control. Every model automatically gets four permissions: add, change, delete, and view. These are named in the format 'app_label.action_modelname' (e.g., 'blog.add_article', 'blog.change_article'). You can also define custom permissions on models.

Permissions can be assigned to individual users or to groups. Groups are collections of permissions that can be assigned to multiple users. For example, an 'Editor' group might have permission to add, change, and view articles but not delete them. When a user belongs to a group, they inherit all of the group's permissions.

The user.has_perm('app.permission') method checks whether a user has a specific permission. Superusers always return True for all permission checks. The @permission_required decorator (FBV) and PermissionRequiredMixin (CBV) protect views by checking permissions before allowing access.

Custom permissions are defined in the model's Meta class using the 'permissions' attribute. They follow the same naming convention and integrate with the admin and permission checking system. Custom permissions are useful for business-specific access control that goes beyond CRUD operations.

In templates, you can check permissions using the {{ perms }} variable: {% if perms.blog.add_article %}. This allows you to show or hide UI elements based on the user's permissions. The perms variable is available automatically through the auth context processor.

Object-level permissions (per-object rather than per-model) are not built into Django but can be implemented using third-party packages like django-guardian or django-rules. Django's built-in permissions are model-level - they control access to all instances of a model, not specific instances.

Code examples

Built-in and Custom Permissions

from django.db import models

class Article(models.Model):
    title = models.CharField(max_length=200)
    is_published = models.BooleanField(default=False)

    class Meta:
        permissions = [
            ('publish_article', 'Can publish article'),
            ('feature_article', 'Can feature article on homepage'),
            ('export_articles', 'Can export articles to CSV'),
        ]

# Django auto-creates these permissions:
# blog.add_article
# blog.change_article
# blog.delete_article
# blog.view_article

# Plus your custom permissions:
# blog.publish_article
# blog.feature_article
# blog.export_articles

Custom permissions are defined in Meta.permissions as a list of (codename, description) tuples. Django auto-creates the four default CRUD permissions. Custom permissions appear in the admin alongside built-in ones.

Checking Permissions in Views

from django.contrib.auth.decorators import permission_required, login_required
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.views.generic import ListView
from django.shortcuts import render
from .models import Article

# FBV with permission check
@permission_required('blog.publish_article', raise_exception=True)
def publish_article(request, pk):
    article = Article.objects.get(pk=pk)
    article.is_published = True
    article.save()
    return redirect('blog:article-detail', pk=pk)

# CBV with permission check
class ArticleListView(PermissionRequiredMixin, ListView):
    model = Article
    permission_required = 'blog.view_article'
    # Multiple permissions:
    # permission_required = ['blog.view_article', 'blog.change_article']

# Manual permission check in view
@login_required
def admin_dashboard(request):
    if not request.user.has_perm('blog.publish_article'):
        return render(request, 'forbidden.html', status=403)
    articles = Article.objects.filter(is_published=False)
    return render(request, 'admin_dashboard.html', {'articles': articles})

@permission_required checks a specific permission. raise_exception=True returns 403 instead of redirecting to login. PermissionRequiredMixin does the same for CBVs. has_perm() allows manual checks in view logic.

Groups and Assigning Permissions

from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
from .models import Article

# Create a group
editor_group = Group.objects.create(name='Editors')

# Get permissions
content_type = ContentType.objects.get_for_model(Article)
permissions = Permission.objects.filter(content_type=content_type)

# Assign permissions to group
editor_group.permissions.add(
    Permission.objects.get(codename='add_article'),
    Permission.objects.get(codename='change_article'),
    Permission.objects.get(codename='view_article'),
    Permission.objects.get(codename='publish_article'),
)

# Add user to group
user.groups.add(editor_group)

# Check user permissions (includes group permissions)
user.has_perm('blog.publish_article')  # True
user.get_all_permissions()
# {'blog.add_article', 'blog.change_article', 'blog.view_article', 'blog.publish_article'}

# Assign permission directly to user
user.user_permissions.add(
    Permission.objects.get(codename='delete_article')
)

Groups bundle permissions for easy assignment. Users inherit group permissions. Direct user permissions are also supported. get_all_permissions() returns the union of group and user permissions.

Permissions in Templates

<!-- Check permissions in templates -->
{% if perms.blog.add_article %}
  <a href="{% url 'blog:article-create' %}">New Article</a>
{% endif %}

{% if perms.blog.change_article %}
  <a href="{% url 'blog:article-edit' pk=article.pk %}">Edit</a>
{% endif %}

{% if perms.blog.delete_article %}
  <form method="post" action="{% url 'blog:article-delete' pk=article.pk %}">
    {% csrf_token %}
    <button type="submit">Delete</button>
  </form>
{% endif %}

{% if perms.blog.publish_article %}
  <button>Publish</button>
{% endif %}

<!-- Check group membership -->
{% if user.groups.all|length %}
  <p>Groups: {{ user.groups.all|join:", " }}</p>
{% endif %}

The {{ perms }} template variable provides permission checking. Use perms.app_label.codename to check specific permissions. This allows showing/hiding UI elements based on the user's access level.

Key points

Concepts covered

Permission, Group, has_perm, permission_required, Custom Permissions