User-specific permissions with per-object support for Django + DRF
Project description
django-user-permissions
User-specific permissions with per-object support for Django + Django REST Framework.
A lightweight, API-friendly object permission system that extends Django’s default permission model with:
- Object-level permissions – grant a permission for a specific instance
- Ownership-based access – automatically grant
view,change,deleteto owners - DRF integration – plug-and-play support for ViewSets/APIs
- Public/safe endpoints – make certain actions public
- Permission caching – request-local + shared cache with automatic invalidation
- Permission syncing – create missing default permissions automatically
Think of it as a simpler alternative to django-guardian for teams building API-first applications with Django REST Framework.
Why not just use django-guardian?
While django-guardian is powerful and battle-tested, it often requires additional setup for DRF-heavy projects.
This package focuses on:
- automatic DRF action mapping
- ownership rules
- safe/public endpoints
- reduced permission boilerplate
Requirements
- Python 3.8+
- Django 3.2+
- Django REST Framework 3.14+
Installation
pip install django-user-permissions
Setup
Add to installed apps
INSTALLED_APPS = [
...
"django_user_permissions",
]
Add authentication backend
Place this before Django’s default backend:
AUTHENTICATION_BACKENDS = [
"django_user_permissions.backends.UserSpecificPermissionBackend",
"django.contrib.auth.backends.ModelBackend",
]
Run migrations
python manage.py migrate
Sync permissions
Create missing permissions:
python manage.py sync_user_permissions
Dry run:
python manage.py sync_user_permissions --dry-run
Configuration
Add this in settings.py:
USER_PERMISSIONS = {
"OWNERSHIP_FIELD": "user",
"OWNER_AUTO_PERMISSIONS": ["view", "change", "delete"],
"ALLOW_CREATE_ON_OWNERSHIP": True,
# Actions that can be resolved at object level
"ALLOW_OBJECT_LEVEL_ACTIONS": [
"retrieve",
"update",
"partial_update",
"destroy"
],
# Allow list endpoints even if final queryset is empty
"LIST_ALLOW_EMPTY": True,
"MODEL_SAFE_METHODS": {
"document": ["list", "retrieve"]
},
"CACHE_TTL": 300
}
DRF Integration
Example model
class Document(models.Model):
title = models.CharField(max_length=255)
user = models.ForeignKey(User, on_delete=models.CASCADE)
Example ViewSet
from django_user_permissions.mixins import UserSpecificPermissionMixin
class DocumentViewSet(UserSpecificPermissionMixin, ModelViewSet):
queryset = Document.objects.all()
serializer_class = DocumentSerializer
auto_filter_queryset = True
auto_filter_queryset=Truefilters the queryset to objects the user can view (global or per-object permissions) plus objects they own—even without explicit permissions.
DRF Action Mapping
| DRF Action | Permission Required | Example |
|---|---|---|
| list | view_<model_name> |
view_document |
| retrieve | view_<model_name> |
view_document |
| create | add_<model_name> |
add_document |
| update | change_<model_name> |
change_document |
| partial_update | change_<model_name> |
change_document |
| destroy | delete_<model_name> |
delete_document |
Example:
PATCH /documents/1/
Requires:
change_document
How Permission Checks Work
Permission evaluation order:
- Superuser access
- Safe/public method access
- Global Django permissions
- Ownership create validation
- Object-specific permissions
- Ownership auto-permissions
Grant Object-Level Permission
from django_user_permissions.models import UserPermission
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
doc = Document.objects.get(pk=1)
perm = Permission.objects.get(codename="change_document")
UserPermission.objects.create(
user=user,
permission=perm,
content_type=ContentType.objects.get_for_model(doc),
object_id=doc.id
)
Now that user can modify only that specific document.
Caching
This package uses:
- request-level caching
- shared caching
- automatic signal-based invalidation
Whenever permissions are updated/deleted, stale cache gets cleared automatically.
Management Commands
python manage.py sync_user_permissions
Supports:
--dry-run--apps--exclude
Testing
pip install -e .[dev]
pytest tests/
Use Cases
- SaaS platforms
- Document sharing apps
- Project management systems
- Multi-tenant products
- Internal admin tools
Roadmap
- Group object permissions
- Queryset filtering helpers
- Admin dashboard improvements
- Async cache invalidation
License
MIT License
Built because writing repetitive permission logic in every DRF project gets exhausting.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_user_permissions-0.1.1.tar.gz.
File metadata
- Download URL: django_user_permissions-0.1.1.tar.gz
- Upload date:
- Size: 16.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1dc483f12ea776421bbbfb2e1be20faa52384a8aa8cb10554e5cdcaa64273ab5
|
|
| MD5 |
444d8380c9d373d86df47a9794b20bce
|
|
| BLAKE2b-256 |
baaa7b881c49d1de4991cb7be618a3c77de65a19ffc733204a27d5649de32202
|
File details
Details for the file django_user_permissions-0.1.1-py3-none-any.whl.
File metadata
- Download URL: django_user_permissions-0.1.1-py3-none-any.whl
- Upload date:
- Size: 17.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ce36003b52c562509b4e70ff19cdb1bcc2390027bf5e056b2f6b3e0d2ba2688d
|
|
| MD5 |
aaaa08b6a0ab82a3ec159a7bb7474d70
|
|
| BLAKE2b-256 |
da63bee64284222295672615673de60cda57d7817413f10d7b5dd0a07b47cf9f
|