用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/Mte90/dotfiles --skill turbodrf命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Django authentication - local accounts, social OAuth, registration, email verification, MFA, session management
django-filter - Django filtering library for querysets with Django REST Framework integration
Build modern dynamic web applications with Django and htmx - partial rendering, HTMX-specific responses, and seamless frontend integration
正在显示 SKILL.md
| name | turbodrf |
| description | TurboDRF - fast Django REST framework with automatic OpenAPI, serializers, views, routers, and caching |
| metadata | {"author":"mte90","version":"1.1.0","tags":["python","django","rest-api","openapi","serializers","caching"]} |
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.
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:
?fields=)pip install turbodrf
# Optional: faster JSON rendering (7x faster than stdlib)
pip install turbodrf[fast]
pip install git+https://github.com/AlexanderCollins/TurboDRF.git
turbodrf[fast] (msgspec/orjson ~7x faster JSON), turbodrf[allauth] (django-allauth >=0.57.0)Verified against TurboDRF v0.5.1 (2026-07-12).
INSTALLED_APPS# settings.py
INSTALLED_APPS = [
# Django apps
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Third-party apps
'rest_framework',
'django_filters',
# TurboDRF
'turbodrf',
# Your apps
'myapp',
]
# 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()
# Define searchable fields
searchable_fields = ['title', 'author']
@classmethod
def turbodrf(cls):
return {
'fields': ['title', 'author', 'price', 'published_date']
}
# urls.py
from django.contrib import admin
from django.urls import path, include
from turbodrf.router import TurboDRFRouter
router = TurboDRFRouter()
urlpatterns = [
# Admin
path('admin/', admin.site.urls),
# API with auto-configured documentation
path('api/', include(router.urls)),
]
# settings.py
TURBODRF_ROLES = {
'admin': [
# Model-level permissions
'myapp.book.read',
'myapp.book.create',
'myapp.book.update',
'myapp.book.delete',
# Field-level permissions
'myapp.book.price.read',
'myapp.book.price.write',
],
'editor': [
'myapp.book.read',
'myapp.book.update',
'myapp.book.price.read', # Read-only access to price
],
'viewer': [
'myapp.book.read',
# No access to price field
]
}
# 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):
# Example: Use Django groups as roles
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/ with:
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
@classmethod
def turbodrf(cls):
return {
'enabled': True, # Enable/disable API (default: True)
'endpoint': 'books', # Custom endpoint name (default: pluralized model name)
'fields': ['title', 'author'], # Fields to expose (see below)
'public_access': False, # Allow unauthenticated GET (default: False)
'lookup_field': 'pk', # URL lookup field (default: 'pk', or 'slug')
'compiled': True, # Use compiled read path (default: True)
}
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', 'author__email', 'price']
}
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 in responses (author__name becomes author_name). M2M fields are arrays of objects:
{
"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 in settings.
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 that access related objects (e.g., self.author.name) won't work in the compiled path — use author__name in the field config instead.
class Book(models.Model, TurboDRFMixin):
title = models.CharField(max_length=200)
author = models.CharField(max_length=100)
description = models.TextField()
price = models.DecimalField(max_digits=10, decimal_places=2)
@classmethod
def turbodrf(cls):
return {
'fields': {
'list': ['title', 'author', 'price'],
'detail': ['title', 'author', 'description', 'price']
}
}
TurboDRF supports three permission modes:
No permissions (development):
TURBODRF_DISABLE_PERMISSIONS = True
Django default permissions:
TURBODRF_USE_DEFAULT_PERMISSIONS = True
Role-based permissions (default):
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',
]
}
app_label.model_name.action (read, create, update, delete)app_label.model_name.field_name.read or .writeprice.read), that field requires explicit permission for ALL rolesprice for viewers, add price.read to at least one role (like admin)TurboDRF reads user.roles — a property that returns a list of role names:
# From Django groups
User.add_to_class('roles', property(lambda self: [g.name for g in self.groups.all()]))
# From a JSONField
class User(AbstractUser):
user_roles = models.JSONField(default=list)
@property
def roles(self):
return self.user_roles
Authenticated users with no roles get 403 on all endpoints.
For runtime changes without redeployment:
TURBODRF_PERMISSION_MODE = 'database'
TURBODRF_PERMISSION_CACHE_TIMEOUT = 300 # 5 minutes
from turbodrf.models import TurboDRFRole, RolePermission, UserRole
role = TurboDRFRole.objects.create(name='editor')
RolePermission.objects.create(role=role, app_label='books', model_name='book', action='read')
UserRole.objects.create(user=user, role=role)
Permissions are checked at each level of a nested field path. For author__publisher__name:
author on Book?publisher on Author?name on Publisher?If any level fails, the field is excluded.
Users can only filter on fields they have read permission for. Filters on hidden fields are silently ignored.
TurboDRF supports custom actions attached to a model's config, inheriting tenant and predicate scoping:
from turbodrf.decorators import turbodrf_action
class Book(models.Model, TurboDRFMixin):
@classmethod
def turbodrf(cls):
return {
'actions': [
turbodrf_action(detail=True, methods=["post"], url_path="resend")(cls.resend)
],
}
def resend(self, request):
# tenant-scoped; predicate stack enforced
...
read_only: True — restricts a model to GET endpoints onlyhttp_methods: ['list', 'retrieve'] — whitelist specific HTTP methodsfull_clean: True — runs model.full_clean() before save@property fields work on both the standard and compiled read paths via DictProxy. Models with unsupported fields (GenericFK) auto-fallback to the serializer path.
The ?role= query parameter is honored in Swagger only for roles the caller actually holds. A user cannot preview another role's permissions.
class Project(models.Model, TurboDRFMixin):
workspace = models.ForeignKey(Workspace, on_delete=models.CASCADE)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
@classmethod
def turbodrf(cls):
return {
'tenant_field': 'workspace', # mandatory wall
'owner_field': 'owner', # within-tenant rule
'bypass_owner_roles': ['manager', 'admin'], # roles ignore owner check
'fields': ['title', 'workspace', 'owner'],
}
```python
TURBODRF_TENANT_MODEL = 'accounts.Workspace'
TURBODRF_TENANT_USER_FIELD = 'workspace' # request.user.workspace → tenant
A request GET /api/projects/ from Alice (member at ABC workspace) goes through:
member has app.project.read. Pass.WHERE project.workspace_id = <Alice's workspace>AND project.owner_id = <Alice's user id>title, workspace, owner but maybe not all configured fields. Hidden ones are removed from the response.# Multi-tenant SaaS — most common case
{'tenant_field': 'store', 'owner_field': 'customer', 'bypass_owner_roles': ['staff']}
# Personal data app (no tenant)
{'owner_field': 'author', 'bypass_owner_roles': ['admin']}
# Reference data (currencies, country codes — not tenant-scoped)
{'tenancy': 'shared'}
# M2M membership (Slack channels, Linear projects)
{'visibility': [Tenant('workspace'), Members('participants')]}
# Power-form composition (when sugar doesn't fit)
{'visibility': [Tenant('workspace'), Either(Owner('owner'), Members('shared_with'))]}
See docs/tenancy.md for the full predicate vocabulary, hard-fail-at-startup behavior, and 404-vs-403 semantics.
Predicate primitives in turbodrf.predicates:
Tenant(field='tenant') — mandatory tenant boundary, applied as AND outside the algebra (OR-composition can never escape it)Owner(field='owner') — row belongs to request.userMembers(field='members') — row's M2M members contains user (read-only: raises NotImplementedError on writes)Group(field='group') — row's group field matches user's group (read-only: raises NotImplementedError on writes)Either(left, right) — logical OR of two predicatesConditional(q_func, write_validator=...) — custom predicate; write_validator is mandatory (read-only if omitted: raises NotImplementedError on writes)Custom(q_func, write_validator=...) — fully custom; write_validator required
Tenant()insideEither()is prohibited — the tenant boundary must not be OR-able away.
Fields like password, token, and secret_key are never exposed:
TURBODRF_SENSITIVE_FIELDS = [
'password', 'password_hash', 'secret_key', 'api_key',
'token', 'access_token', 'refresh_token', 'session_key',
]
If a permission check fails due to an error, access is denied. TurboDRF never grants access on exception.
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'turbodrf.exceptions.turbodrf_exception_handler',
}
{
"error": {
"status": 403,
"code": "permission_denied",
"message": "You do not have permission to perform this action."
}
}
TurboDRF runs 5 startup safety passes; each has a kill-switch setting:
tenant_field/visibility/tenancy: 'shared' (kill-switch: TURBODRF_REQUIRE_TENANCY=False)TURBODRF_ALLOW_UNSAFE_COMPILED_FK)TURBODRF_ALLOW_UNSAFE_COMPILED_M2M)TURBODRF_ALLOW_UNSAFE_FILTER_TRAVERSAL)Custom/Conditional must carry explicit write_validator (kill-switch: TURBODRF_ALLOW_UNSAFE_CUSTOM_WRITE)TURBODRF_ROLES with difflib suggestions (kill-switch: TURBODRF_ALLOW_UNKNOWN_PERMISSIONS)Additional protections:
Members, Group, and Conditional predicates raise NotImplementedError on writes (read-only enforcement)TURBODRF_MAX_FILTER_VALUE_LENGTH (default 1000) caps filter value lengthTURBODRF_LOG_UNRESTRICTED_CUSTOM (default True) logs when unrestricted custom predicates runThese gates refuse to boot if unsafe configurations are detected, preventing cross-permission read leaks.
Auto-generated Swagger UI and ReDoc:
/api/swagger//api/redoc/Disable in production:
TURBODRF_ENABLE_DOCS = False
# Validate configuration
python manage.py turbodrf_check
# Performance benchmark
python manage.py turbodrf_benchmark
# Explain query execution
python manage.py turbodrf_explain
Key settings:
| Setting | Description |
|---|---|
TURBODRF_ROLES | Role-based permissions dict |
TURBODRF_DISABLE_PERMISSIONS | Disable all permissions |
TURBODRF_USE_DEFAULT_PERMISSIONS | Use Django default permissions |
TURBODRF_PERMISSION_MODE | 'static' or 'database' |
TURBODRF_PERMISSION_CACHE_TIMEOUT | Permission cache TTL (seconds) |
TURBODRF_TENANT_MODEL | Default tenant model |
TURBODRF_TENANT_USER_FIELD | User's tenant field |
TURBODRF_SENSITIVE_FIELDS | Fields to hide from all users |
TURBODRF_ENABLE_DOCS | Enable Swagger/ReDoc |
TURBODRF_MAX_NESTING_DEPTH | Max nested field depth |
TURBODRF_USE_FILTERS | Enable Django filters |
TURBODRF_DEFAULT_PAGE_SIZE | Default pagination page size |
TURBODRF_REQUIRE_TENANCY | Force tenancy declaration on all models (default True) |
TURBODRF_ALLOW_UNSAFE_COMPILED_FK | Kill-switch for compiled FK safety gate |
TURBODRF_ALLOW_UNSAFE_COMPILED_M2M | Kill-switch for compiled M2M safety gate |
TURBODRF_ALLOW_UNSAFE_FILTER_TRAVERSAL | Kill-switch for filter traversal safety gate |
TURBODRF_ALLOW_UNSAFE_CUSTOM_WRITE | Kill-switch for custom predicate write-safety gate |
TURBODRF_ALLOW_UNKNOWN_PERMISSIONS | Kill-switch for permission string typo check |
TURBODRF_MAX_FILTER_VALUE_LENGTH | Max filter value length (default 1000) |
TURBODRF_LOG_UNRESTRICTED_CUSTOM | Log unrestricted custom predicates (default True) |
models.py:
from django.db import models
from turbodrf.mixins import TurboDRFMixin
class Author(models.Model, TurboDRFMixin):
name = models.CharField(max_length=100)
email = models.EmailField()
@classmethod
def turbodrf(cls):
return {
'fields': ['name', 'email']
}
class Book(models.Model, TurboDRFMixin):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
price = models.DecimalField(max_digits=10, decimal_places=2)
@classmethod
def turbodrf(cls):
return {
'fields': {
'list': ['title', 'author__name'],
'detail': ['title', 'author__name', 'author__email', 'price']
}
}
settings.py:
INSTALLED_APPS = [
'rest_framework',
'turbodrf',
'myapp',
]
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',
]
}
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)),
]
Define fields explicitly rather than __all__ for better control.
Use Django's form validation or custom validators:
TurboDRF ships optional, experimental integrations (all settings-gated, marked experimental in docs):
STRICT_ROLES=True default)pip install turbodrf[allauth])Fast JSON: pip install turbodrf[fast] adds msgspec (~7x faster serialization; orjson auto-detected if present).
The TurboDRF repository ships an AGENTS.md with canonical guidance for AI coding agents — the "what never to do" list (don't override get_queryset, don't use Model.objects.all() in custom actions, don't widen serializer fields, don't use tenancy: 'shared' as a test workaround) plus invalidate_user_permissions() cache API and testing patterns.
Docs status: The readthedocs and GitHub Pages sites are currently 404. The repo
docs/folder is the only current documentation source.
### 3. Use Permissions
Restrict access appropriately:
```python
TURBODRF_ROLES = {
'public': ['myapp.book.read'],
'staff': [
'myapp.book.read',
'myapp.book.create',
'myapp.book.update',
'myapp.book.delete',
],
}
Users can only filter on fields they have read permission for.
Always include sensitive fields in the deny-list:
TURBODRF_SENSITIVE_FIELDS = [
'password', 'token', 'api_key', 'secret_key',
]
If you get import errors, ensure TurboDRF is properly installed:
pip list | grep turbodrf
If no endpoints appear, check that:
TurboDRFMixinturbodrf() classmethod'enabled': False)If you get 403 errors:
TURBODRF_ROLESroles propertyIf startup gate fires due to M2M/FK traversals:
turbodrf() fields list'compiled': False on the parent modelTURBODRF_ALLOW_UNSAFE_COMPILED_M2M = True (not recommended)