API Reference
Complete reference for Aksara's API layer.
ViewSets
ModelViewSet
Full CRUD ViewSet for a model.
from aksara.api import ModelViewSet
class UserViewSet(ModelViewSet):
model = User
serializer_class = UserSerializer
permission_classes = [IsAuthenticated]
filterset_fields = ["is_active", "role"]
search_fields = ["name", "email"]
ordering_fields = ["created_at", "name"]
ordering = ["-created_at"]
Attributes
| Attribute |
Type |
Description |
model |
Model |
The model class |
serializer_class |
Serializer |
Default serializer |
permission_classes |
list |
Permission classes |
filterset_fields |
list |
Filterable fields |
search_fields |
list |
Searchable fields |
ordering_fields |
list |
Fields for ordering |
ordering |
list |
Default ordering |
pagination_class |
Pagination |
Pagination class |
lookup_field |
str |
Lookup field (default: "id") |
Methods
| Method |
Description |
get_queryset() |
Return the queryset |
get_object() |
Get single object |
get_serializer_class() |
Return serializer class |
get_permissions() |
Return permission instances |
perform_create(serializer) |
Called on create |
perform_update(serializer) |
Called on update |
perform_destroy(instance) |
Called on delete |
Actions
| Action |
Method |
URL |
list |
GET |
/ |
create |
POST |
/ |
retrieve |
GET |
/{id}/ |
update |
PUT |
/{id}/ |
partial_update |
PATCH |
/{id}/ |
destroy |
DELETE |
/{id}/ |
ViewSet
Base ViewSet without default actions.
from aksara.api import ViewSet, action
class CustomViewSet(ViewSet):
@action(detail=False, methods=["get"])
async def custom_list(self, request):
return {"data": []}
@action(detail=True, methods=["post"])
async def custom_action(self, request, pk=None):
return {"id": pk}
ReadOnlyModelViewSet
Read-only ViewSet (list and retrieve only).
from aksara.api import ReadOnlyModelViewSet
class PostViewSet(ReadOnlyModelViewSet):
model = Post
serializer_class = PostSerializer
Actions
@action Decorator
from aksara.api import action
@action(
detail=True, # True for /items/{id}/action
methods=["post"], # HTTP methods
url_path="custom-path", # Custom URL path
url_name="custom_name", # Custom URL name
permission_classes=[IsAdmin], # Override permissions
serializer_class=CustomSerializer, # Override serializer
)
async def my_action(self, request, pk=None):
return {"success": True}
Parameters
| Parameter |
Type |
Default |
Description |
detail |
bool |
Required |
True for detail route |
methods |
list |
["get"] |
HTTP methods |
url_path |
str |
Method name |
URL path |
url_name |
str |
Method name |
URL name |
permission_classes |
list |
None |
Override permissions |
serializer_class |
class |
None |
Override serializer |
Serializers
ModelSerializer
from aksara.api import ModelSerializer
class UserSerializer(ModelSerializer):
full_name = SerializerMethodField()
class Meta:
model = User
fields = ["id", "email", "name", "full_name", "created_at"]
read_only_fields = ["id", "created_at"]
extra_kwargs = {
"password": {"write_only": True},
}
async def get_full_name(self, obj):
return f"{obj.first_name} {obj.last_name}"
| Option |
Type |
Description |
model |
Model |
Model class |
fields |
list/str |
Fields to include ("__all__" for all) |
exclude |
list |
Fields to exclude |
read_only_fields |
list |
Read-only fields |
extra_kwargs |
dict |
Per-field options |
Methods
| Method |
Description |
validate_<field>(value) |
Validate single field |
validate(data) |
Cross-field validation |
create(validated_data) |
Create instance |
update(instance, validated_data) |
Update instance |
to_representation(instance) |
Convert to output |
to_internal_value(data) |
Convert from input |
Serializer
Aksara serializers use Pydantic under the hood, so fields are inferred from your model automatically:
from aksara.api import ModelSerializer
class UserSerializer(ModelSerializer):
class Meta:
model = User
fields = ["id", "email", "name", "is_active"]
read_only_fields = ["id", "created_at"]
def validate_email(self, value):
# Custom field validation
return value.lower()
async def validate(self, data):
# Cross-field validation
return data
| Option |
Type |
Description |
model |
Model class |
The Aksara model to serialize |
fields |
list or "__all__" |
Fields to include |
exclude |
list |
Fields to exclude |
read_only_fields |
list |
Fields in output only |
expand |
list or dict |
FK expansion configuration |
SerializerMethodField
For computed fields:
class PostSerializer(ModelSerializer):
author_name = SerializerMethodField()
class Meta:
model = Post
fields = ["id", "title", "author_name"]
async def get_author_name(self, obj):
author = await obj.author
return author.name
---
## Permissions
### Built-in Permissions
```python
from aksara.api.permissions import (
AllowAny,
IsAuthenticated,
IsAdminUser,
IsAuthenticatedOrReadOnly,
)
| Permission |
Description |
AllowAny |
Allow all requests |
IsAuthenticated |
Require authentication |
IsAdminUser |
Require admin user |
IsAuthenticatedOrReadOnly |
Auth for writes |
Custom Permission
from aksara.api.permissions import BasePermission
class IsOwner(BasePermission):
async def has_object_permission(self, request, view, obj):
return obj.owner_id == request.user.id
class HasSubscription(BasePermission):
async def has_permission(self, request, view):
return request.user.has_active_subscription
Permission Methods
| Method |
Description |
has_permission(request, view) |
Check view-level access |
has_object_permission(request, view, obj) |
Check object-level access |
Authentication
Token Authentication
from aksara.api.authentication import TokenAuthentication
# In settings
"DEFAULT_AUTHENTICATION_CLASSES": [
"aksara.api.authentication.TokenAuthentication",
]
Custom Authentication
from aksara.api.authentication import BaseAuthentication
class APIKeyAuthentication(BaseAuthentication):
async def authenticate(self, request):
api_key = request.headers.get("X-API-Key")
if not api_key:
return None
user = await User.objects.filter(api_key=api_key).first()
if user:
return (user, None)
return None
from aksara.api.pagination import PageNumberPagination
class CustomPagination(PageNumberPagination):
page_size = 20
page_size_query_param = "page_size"
max_page_size = 100
Response format:
{
"count": 100,
"next": "/api/posts/?page=2",
"previous": null,
"results": [...]
}
from aksara.api.pagination import LimitOffsetPagination
class CustomPagination(LimitOffsetPagination):
default_limit = 20
max_limit = 100
from aksara.api.pagination import CursorPagination
class CustomPagination(CursorPagination):
ordering = "-created_at"
page_size = 20
Filtering
FilterSet
from aksara.api.filters import FilterSet, Filter
class PostFilterSet(FilterSet):
author = Filter(field_name="author_id")
published = Filter(field_name="is_published")
created_after = Filter(field_name="created_at", lookup="gte")
title_contains = Filter(field_name="title", lookup="contains")
class Meta:
model = Post
class PostViewSet(ModelViewSet):
filterset_class = PostFilterSet
Filter Lookups
| Lookup |
SQL |
Example |
exact |
= |
?status=active |
iexact |
ILIKE |
?name__iexact=john |
contains |
LIKE %x% |
?title__contains=python |
icontains |
ILIKE %x% |
?title__icontains=python |
gt |
> |
?price__gt=100 |
gte |
>= |
?price__gte=100 |
lt |
< |
?price__lt=100 |
lte |
<= |
?price__lte=100 |
in |
IN |
?status__in=a,b,c |
isnull |
IS NULL |
?deleted_at__isnull=true |
Routing
URL Configuration
from aksara import include_viewset
from .views import UserViewSet, PostViewSet
# List your ViewSets here
urlpatterns = [
UserViewSet,
PostViewSet,
]
def register_routes(app):
for viewset in urlpatterns:
include_viewset(app, viewset)
Auto-Discovery (v0.3.14+)
from aksara.api import include_app_viewsets, include_all_app_viewsets
# Register all ViewSets in a specific app
include_app_viewsets(app, "blog")
# Register all ViewSets across all INSTALLED_APPS
include_all_app_viewsets(app)
Manual Routes
For endpoints outside of ViewSets, use FastAPI's APIRouter directly:
from fastapi import APIRouter
router = APIRouter()
@router.get("/custom")
async def custom_endpoint(request):
return {"data": "value"}
@router.post("/custom/{id}")
async def custom_action(request, id: str):
return {"id": id}
# Include in your Aksara app
app.include_router(router)
Request & Response
Request Object
async def my_view(request):
# User
user = request.user
is_auth = request.user.is_authenticated
# Data
data = request.data # Parsed body
query = request.query_params # Query string
# Headers
auth = request.headers.get("Authorization")
# Method
method = request.method # GET, POST, etc.
from aksara.api import Response
# JSON response (default)
return {"data": "value"}
# Custom status
return {"error": "Not found"}, 404
# Response object
return Response(
data={"key": "value"},
status=201,
headers={"X-Custom": "header"}
)
Throttling
Built-in Throttles
from aksara.api.throttling import AnonRateThrottle, UserRateThrottle
class PostViewSet(ModelViewSet):
throttle_classes = [AnonRateThrottle, UserRateThrottle]
Custom Throttle
from aksara.api.throttling import BaseThrottle
class BurstThrottle(BaseThrottle):
rate = "60/minute"
def get_cache_key(self, request, view):
return f"throttle:{request.user.id}"