turbodrf
TurboDRF - Dead simple Django REST API generator with role-based permissions
Turn your Django models into fully-featured REST APIs with a mixin and a configuration method. Zero boilerplate.
Overview
TurboDRF is a Django REST Framework mixin-based library that automatically generates CRUD API endpoints for your models. Unlike traditional DRF setups requiring ViewSets and serializers, TurboDRF uses a simple mixin pattern where you declare your model inherits from TurboDRFMixin and define a turbodrf() configuration method.
Key Features:
- Automatic CRUD endpoints from model declaration
- Role-based access control (RBAC)
- Field-level permissions
- Built-in search, filtering, ordering, and pagination
- Nested field support for relationships
- Client-side field selection (
?fields=)
- Auto-generated API documentation (Swagger UI, ReDoc)
- Performance optimizations with compiled read path
- Security: sensitive fields deny-list, FK injection defense, startup safety gates
Installation
PyPI
pip install turbodrf
# Optional: faster JSON rendering (7x faster than stdlib)
pip install turbodrf[fast]
GitHub
pip install git+https://github.com/AlexanderCollins/TurboDRF.git
Requirements
- Python >=3.10 (tested: 3.10, 3.11, 3.12, 3.13, 3.14)
- Django >=4.2 (tested: 4.2, 5.2, 6.0)
- Django REST Framework >=3.14.0
- drf-yasg >=1.21.0, django-filter >=23.0
- Optional extras:
turbodrf[fast] (msgspec/orjson ~7x faster), turbodrf[allauth] (django-allauth >=0.57.0)
Verified against TurboDRF v0.5.1 (2026-07-12).
Quick Start
1. Add to INSTALLED_APPS
# settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'django_filters',
'turbodrf',
'myapp',
]
2. Add the mixin to your model
# myapp/models.py
from django.db import models
from turbodrf.mixins import TurboDRFMixin
class Book(models.Model, TurboDRFMixin):
title = models.CharField(max_length=200)
author = models.CharField(max_length=100)
price = models.DecimalField(max_digits=10, decimal_places=2)
published_date = models.DateField()
searchable_fields = ['title', 'author']
@classmethod
def turbodrf(cls):
return {
'fields': ['title', 'author', 'price', 'published_date']
}
3. Add the router
# urls.py
from django.contrib import admin
from django.urls import path, include
from turbodrf.router import TurboDRFRouter
router = TurboDRFRouter()
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include(router.urls)),
]
4. Configure TurboDRF roles
# settings.py
TURBODRF_ROLES = {
'admin': [
'myapp.book.read',
'myapp.book.create',
'myapp.book.update',
'myapp.book.delete',
'myapp.book.price.read',
'myapp.book.price.write',
],
'editor': [
'myapp.book.read',
'myapp.book.update',
'myapp.book.price.read',
],
'viewer': [
'myapp.book.read',
]
}
5. Extend User Model with Roles
# myapp/apps.py
from django.apps import AppConfig
from django.contrib.auth import get_user_model
class MyAppConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'myapp'
def ready(self):
User = get_user_model()
def get_user_roles(self):
return [group.name for group in self.groups.all()]
if not hasattr(User, 'roles'):
User.add_to_class('roles', property(get_user_roles))
Done! You now have a full REST API at /api/:
GET /api/books/ # List all books
POST /api/books/ # Create a new book
GET /api/books/1/ # Get a specific book
PUT /api/books/1/ # Update a book
DELETE /api/books/1/ # Delete a book
Query parameters:
GET /api/books/?search=django # Search
GET /api/books/?author__name=Smith # Filter
GET /api/books/?ordering=-price # Order
GET /api/books/?page=2&page_size=10 # Paginate
GET /api/books/?fields=title,price # Client field selection
Model Configuration
Decision: How do I control which fields and endpoints are exposed?
Configure your model's API surface with the turbodrf() classmethod. For the complete option reference, see references/configuration.md.
Meta Options
All options available in the turbodrf() classmethod:
@classmethod
def turbodrf(cls):
return {
'enabled': True, # Enable/disable API (default: True)
'endpoint': 'books', # Custom endpoint name
'fields': ['title', 'author'], # Fields to expose
'public_access': False, # Allow unauthenticated GET
'lookup_field': 'pk', # URL lookup field ('pk' or 'slug')
'compiled': True, # Use compiled read path
}
Fields Specification
Decision: Do I want all fields or a curated subset?
All database fields:
'fields': '__all__'
Specific fields (same for list and detail):
'fields': ['title', 'author', 'price']
Different fields for list vs detail:
'fields': {
'list': ['title', 'author', 'price'],
'detail': ['title', 'description', 'author__email', 'price']
}
Nested Fields
Access related model fields with __ notation:
'fields': [
'title',
'author__name', # ForeignKey (1 level)
'author__publisher__name', # Multi-level (2 levels)
'tags__name', # ManyToMany
]
FK fields are flattened (author__name → author_name). M2M fields are arrays:
{
"title": "Django for APIs",
"author_name": "William Vincent",
"tags": [{"name": "Python"}, {"name": "Django"}]
}
Maximum nesting depth is 3 by default. Change with TURBODRF_MAX_NESTING_DEPTH.
Property Fields
Model @property methods work in the compiled path:
class Book(models.Model, TurboDRFMixin):
title = models.CharField(max_length=200)
price = models.DecimalField(max_digits=10, decimal_places=2)
@property
def display_title(self):
return self.title.upper()
@classmethod
def turbodrf(cls):
return {
'fields': ['title', 'price', 'display_title']
}
Properties accessing related objects won't work in compiled path — use author__name instead.
List/Detail Field Separation
class Book(models.Model, TurboDRFMixin):
title = models.CharField(max_length=200)
description = models.TextField()
price = models.DecimalField(max_digits=10, decimal_places=2)
@classmethod
def turbodrf(cls):
return {
'fields': {
'list': ['title', 'price'],
'detail': ['title', 'description', 'price']
}
}
Documentation
Auto-generated Swagger UI and ReDoc:
- Swagger UI:
/api/swagger/
- ReDoc:
/api/redoc/
Disable in production:
TURBODRF_ENABLE_DOCS = False
Management Commands
Decision: How do I validate my configuration and debug issues?
# Validate configuration
python manage.py turbodrf_check
# Expected healthy output:
# "✓ All models declared with turbodrf()"
# "✓ Tenancy gates passed"
# "✓ No unsafe FK/M2M traversals detected"
# "Configuration valid."
# Expected misconfiguration output:
# "✗ Model 'MyModel' missing tenant_field or visibility declaration"
# "✗ Unsafe M2M path detected: Book.tags -> Tag.secret_data"
# "Configuration invalid. Fix errors before deployment."
# Performance benchmark
python manage.py turbodrf_benchmark
# Expected output:
# "Compiled path: 0.003s per request"
# "Serializer path: 0.021s per request"
# "Speedup: 7x"
# Explain query execution
python manage.py turbodrf_explain --model Book --query "search=django"
# Expected output:
# "Query: SELECT ... FROM books_book"
# "Tenant filter: workspace_id = 42"
# "Predicate: (owner_id = 7) OR (workspace_id IN (SELECT id FROM workspace_members ...))"
# "Fields projected: ['title', 'author__name', 'price']"
Integrations
TurboDRF ships optional, experimental integrations (all settings-gated):
- Sentry — security-event breadcrumbs
- Keycloak — role mapping (
STRICT_ROLES=True default)
- django-allauth — group→role mapping (
pip install turbodrf[allauth])
- drf-api-tracking — request logging
Fast JSON: pip install turbodrf[fast] adds msgspec (~7x faster serialization).
AI Agent Guidance
Decision: What rules must I follow when generating TurboDRF code?
Startup-Gate Kill-Switches
Never disable safety gates in production. Each gate has a kill-switch for emergencies only:
TURBODRF_REQUIRE_TENANCY=False — allows models without tenant/visibility declaration
TURBODRF_ALLOW_UNSAFE_COMPILED_FK=True — bypasses FK annotation safety
TURBODRF_ALLOW_UNSAFE_COMPILED_M2M=True — bypasses M2M traversal safety
TURBODRF_ALLOW_UNSAFE_FILTER_TRAVERSAL=True — allows filter join leaks
TURBODRF_ALLOW_UNSAFE_CUSTOM_WRITE=True — allows custom predicates without write validators
TURBODRF_ALLOW_UNKNOWN_PERMISSIONS=True — disables typo checking
Tenant-Scoping Invariants
- Prohibition: Never write
Either(Tenant('workspace'), Owner('owner')) — the tenant boundary must not be OR-able away. Use visibility: [Tenant('workspace'), Either(Owner('owner'), Members('shared_with'))] instead.
- Mandatory: Every shared-tenant model must declare
tenant_field, visibility, or tenancy: 'shared'.
- Read-only predicates:
Members() and Group() raise NotImplementedError on writes.
Predicate Vocabulary Contract
When generating row-level access rules, use only these primitives from turbodrf.predicates:
Tenant(field) — mandatory tenant boundary
Owner(field) — row belongs to request.user
Members(field) — M2M contains user (read-only)
Group(field) — group field matches user's group (read-only)
Either(left, right) — logical OR
Conditional(q_func, write_validator=...) — custom with mandatory write validator
Custom(q_func, write_validator=...) — fully custom with mandatory write validator
Cache API
After bulk permission changes, call invalidate_user_permissions(user) to clear the permission cache.
Best Practices
1. Use Meta Options
Define fields explicitly rather than __all__ for better control.
2. Validate Input
Use Django's form validation or custom validators:
def validate_title(value):
if Book.objects.filter(title=value).exclude(pk=self.instance.pk).exists():
raise serializers.ValidationError("Title already exists")
return value
3. Use Permissions
Restrict access appropriately:
TURBODRF_ROLES = {
'public': ['myapp.book.read'],
'staff': [
'myapp.book.read',
'myapp.book.create',
'myapp.book.update',
'myapp.book.delete',
],
}
4. Filter Usage
Users can only filter on fields they have read permission for.
5. Secure Sensitive Data
Always include sensitive fields in the deny-list:
TURBODRF_SENSITIVE_FIELDS = [
'password', 'token', 'api_key', 'secret_key',
]
Deep Dives
The following reference documents are loaded on demand from references/:
- references/configuration.md — Complete Meta options, settings reference, startup safety gates
- references/permissions-tenancy.md — Role-based permissions, field-level access, multi-tenant predicates, row-level scoping
- references/security-settings.md — Security gates, fail-closed design, troubleshooting
- references/examples.md — Complete CRUD API examples with nested relationships
References