Django REST Framework Role Filters

1.1.0 · active · verified Sun Apr 12

djangorestframework-role-filters is a Python library that provides simple and declarative role-based filtering for Django REST Framework views and querysets. It aims to eliminate the need for verbose 'if-else' statements in view logic by centralizing role definitions. The current version is 1.1.0, with releases occurring periodically, typically to update compatibility with newer Django/DRF versions.

Warnings

Install

Imports

Quickstart

This quickstart demonstrates how to define role-specific filters using `RoleFilter` subclasses and apply them to a `RoleFilterModelViewSet`. Each `RoleFilter` defines allowed actions, queryset filtering, and serializer classes based on a `role_id`. The `get_role_id` method on the `ViewSet` dynamically determines the user's role.

import os
from django.db import models
from django.contrib.auth.models import AbstractUser
from rest_framework import serializers
from rest_framework_role_filters.role_filters import RoleFilter
from rest_framework_role_filters.viewsets import RoleFilterModelViewSet

# --- Mock Django Setup (for runnable example) ---
# This is usually handled by a real Django project setup
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings') # Replace with your actual settings
import django
django.setup()

# --- Mock Models ---
class User(AbstractUser):
    ROLE_CHOICES = (
        ('admin', 'Admin'),
        ('user', 'User'),
    )
    role = models.CharField(max_length=10, choices=ROLE_CHOICES, default='user')

class Post(models.Model):
    title = models.CharField(max_length=255)
    body = models.TextField()
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.title

# --- Mock Serializers ---
class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ['id', 'title', 'body', 'user', 'created_at', 'updated_at']
        read_only_fields = ['user']

class PostSerializerForUser(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ['id', 'title', 'body', 'created_at', 'updated_at']
        read_only_fields = ['user']

# --- Role Filters (role_filters.py) ---
class AdminRoleFilter(RoleFilter):
    role_id = 'admin'

    def get_allowed_actions(self, request, view, obj=None):
        # Admins can do anything
        return ['create', 'list', 'retrieve', 'update', 'partial_update', 'destroy']

    def get_queryset(self, request, view, queryset):
        # Admins see all posts
        return queryset.all()

    def get_serializer_class(self, request, view):
        return PostSerializer

class UserRoleFilter(RoleFilter):
    role_id = 'user'

    def get_allowed_actions(self, request, view, obj=None):
        # Users can create, list, retrieve, update their own posts
        return ['create', 'list', 'retrieve', 'update', 'partial_update']

    def get_queryset(self, request, view, queryset):
        # Users only see their own posts
        return queryset.filter(user=request.user)

    def get_serializer_class(self, request, view):
        return PostSerializerForUser

    def get_serializer(self, request, view, serializer_class, *args, **kwargs):
        # Example of dynamically modifying serializer fields for a user role
        fields = ('body', 'created_at', 'id', 'title', 'updated_at')
        return serializer_class(*args, fields=fields, **kwargs)

# --- ViewSet (views.py) ---
class PostViewSet(RoleFilterModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
    role_filter_classes = [AdminRoleFilter, UserRoleFilter]

    def get_role_id(self, request):
        # This method is crucial: it determines the role for the current request
        # In a real app, request.user would be an authenticated user object.
        # For this example, we assume request.user has a 'role' attribute.
        # You might use request.user.is_staff or custom logic here.
        if request.user.is_authenticated:
            return request.user.role
        return 'anonymous' # Fallback or specific anonymous role

    def perform_create(self, serializer):
        serializer.save(user=self.request.user)

# Example usage (not directly runnable without Django/DRF server):
# from rest_framework.test import APIRequestFactory
# from django.contrib.auth.models import AnonymousUser
#
# factory = APIRequestFactory()
#
# # Simulate an admin user
# admin_user = User(username='admin', role='admin', is_authenticated=True)
# request = factory.get('/posts/')
# request.user = admin_user
# view = PostViewSet.as_view({'get': 'list'})
# response = view(request)
# print(f"Admin response status: {response.status_code}")
#
# # Simulate a regular user
# regular_user = User(username='user1', role='user', is_authenticated=True)
# request = factory.get('/posts/')
# request.user = regular_user
# view = PostViewSet.as_view({'get': 'list'})
# response = view(request)
# print(f"User response status: {response.status_code}")

# In a real Django setup, you would add PostViewSet to your urls.py:
# from django.urls import path, include
# from rest_framework.routers import DefaultRouter
#
# router = DefaultRouter()
# router.register(r'posts', PostViewSet)
#
# urlpatterns = [
#     path('api/', include(router.urls)),
# ]

view raw JSON →