drf-turbo
An alternative serializer implementation for REST framework written in cython built for speed.
- Free software: MIT license
- Documentation: https://drf-turbo.readthedocs.io.
NOTE: Cython is required to build this package.
Requirements
- Django
- Django REST Framework
- Cython
- forbiddenfruit
- pyyaml(OpenAPI)
- uritemplate(OpenAPI)
- djangorestframework-simplejwt(OpenAPI)
Installation
$ pip install drf-turbo
Performance
https://drf-turbo.readthedocs.io/en/latest/performance.html
Examples
Declaring Serializers
from datetime import datetime
from django.utils.timezone import now
import drf_turbo as dt
class User:
def __init__(self, username, email,created=None):
self.username = username
self.email = email
self.created = created or datetime.now()
user = User(username='test' , email='[email protected]')
class UserSerializer(dt.Serializer):
username = dt.StrField(max_length=50)
email = dt.EmailField()
created = dt.DateTimeField()
Serializing objects
serializer = UserSerializer(user)
serializer.data
# {'username': 'test', 'email': '[email protected]', 'created': '2021-11-04T22:49:01.981127Z'}
Deserializing objects
data = {'username':'new_test','email':'[email protected]','created':now()}
serializer = UserSerializer(data=data)
serializer.is_valid()
# True
serializer.validated_data
# {'username': 'new_test', 'email': '[email protected]', 'created': datetime.datetime(2021, 11, 12, 6, 10, 44, 85118)}}
Validation
serializer = UserSerializer(data={'email': 'test'})
serializer.is_valid()
# False
serializer.errors
# {'username': ['This field is required.'], 'email': ['Enter a valid email address.'],'created': ['This field is required.']}
Field-level validation
import drf_turbo as dt
class UserSerializer(dt.Serializer):
username = dt.StrField(max_length=50)
def validate_username(self, value):
if 'test' not in value.lower():
raise dt.ValidationError("test must be in username")
return value
Object-level validation
import drf_turbo as dt
class CampaignSerializer(dt.Serializer):
start_date = dt.DateTimeField()
end_date = dt.DateTimeField()
def validate(self, data):
if data['start_date'] > data['end_date']:
raise dt.ValidationError("start_date must occur before end_date")
return data
Nested Serializers
from datetime import datetime
from django.utils.timezone import now
import drf_turbo as dt
class User:
def __init__(self, username, email,created=None):
self.username = username
self.email = email
self.created = created or datetime.now()
user = User(username='test' , email='[email protected]')
class UserSerializer(dt.Serializer):
username = dt.StrField(max_length=50)
email = dt.EmailField()
created = dt.DateTimeField()
class Profile :
def __init__(self, age=25):
self.age = age
self.user = user
profile = Profile()
class ProfileSerializer(dt.Serializer):
age = dt.IntField()
user = UserSerializer()
serializer = ProfileSerializer(profile)
serializer.data
# {'age' : 25 , 'user' : {'username': 'test', 'email': '[email protected]', 'created': '2021-11-04T22:49:01.981127Z'}}
Filtering Output
drf-turbo provides option to enclude or exclude fields from serializer using only
or exclude
keywords.
serializer = UserSerializer(user,only=('id','username'))
or
serializer = ProfileSerializer(profile,exclude=('id','user__email'))
or
http://127.0.0.1:8000/user/?only=id,username
Required Fields
Make a field required by passing required=True. An error will be raised if the the value is missing from data during Deserializing.
For example:
class UserSerializer(dt.Serializer):
username = dt.StrField(required=True,error_messages={"required":"no username"})
Specifying Defaults
It will be used for the field if no input value is supplied.
For example:
from datetime import datetime
class UserSerializer(dt.Serializer):
birthdate = dt.DateTimeField(default=datetime(2021, 11, 05))
ModelSerializer
Mapping serializer to Django model definitions.
Features :
- It will automatically generate a set of fields for you, based on the model.
- It will automatically generate validators for the serializer.
- It includes simple default implementations of .create() and .update().
class UserSerializer(dt.ModelSerializer):
class Meta :
model = User
fields = ('id','username','email')
You can also set the fields attribute to the special value __all__
to indicate that all fields in the model should be used.
For example:
class UserSerializer(dt.ModelSerializer):
class Meta :
model = User
fields = '__all__'
You can set the exclude attribute to a list of fields to be excluded from the serializer.
For example:
class UserSerializer(dt.ModelSerializer):
class Meta :
model = User
exclude = ('email',)
Read&Write only fields
class UserSerializer(dt.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'password','password_confirmation')
read_only_fields = ('username')
write_only_fields = ('password','password_confirmation')
Parsers
Allow only requests with JSON content, instead of the default of JSON or form data.
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': [
'drf_turbo.parsers.JSONParser',
]
}
or
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': [
'drf_turbo.parsers.UJSONParser',
]
}
or
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': [
'drf_turbo.parsers.ORJSONParser',
]
}
NOTE: ujson must be installed to use UJSONParser.
NOTE: orjson must be installed to use ORJSONParser.
Renderers
Use JSON as the main media type.
REST_FRAMEWORK = {
'DEFAULT_RENDERERS_CLASSES': [
'drf_turbo.renderers.JSONRenderer',
]
}
or
REST_FRAMEWORK = {
'DEFAULT_RENDERERS_CLASSES': [
'drf_turbo.renderers.UJSONRenderer',
]
}
or
REST_FRAMEWORK = {
'DEFAULT_RENDERERS_CLASSES': [
'drf_turbo.renderers.ORJSONRenderer',
]
}
NOTE: ujson must be installed to use UJSONRenderer.
NOTE: orjson must be installed to use ORJSONRenderer.
Responses
An HttpResponse
subclass that helps to create a JSON-encoded response. Its default Content-Type header is set to application/json.
from rest_framework.views import APIView
import drf_turbo as dt
class UserInfo(APIView):
def get(self,request):
data = {"username":"test"}
return dt.JsonResponse(data,status=200)
or
class UserInfo(APIView):
def get(self,request):
data = {"username":"test"}
return dt.UJSONResponse(data,status=200)
or
class UserInfo(APIView):
def get(self,request):
data = {"username":"test"}
return dt.ORJSONResponse(data,status=200)
NOTE: ujson must be installed to use UJSONResponse.
NOTE: orjson must be installed to use ORJSONResponse.
Also drf-turbo provides an easy way to return a success or error response using SuccessResponse
or ErrorResponse
clasess.
for example :
class UserInfo(APIView):
def get(self,request):
data = {"username":"test"}
serializer = UserSerializer(data=data)
if not serializer.is_valid():
return dt.ErrorResponse(serializer.errors)
# returned response : {'message':'Bad request', data : ``serializer_errros``, 'error': True} with status = 400
return dt.SuccessResponse(data)
# returned response : {'message':'Success', data : {"username":"test"} , 'error': False} with status = 200
OpenApi(Swagger)
Add drf-turbo to installed apps in settings.py
INSTALLED_APPS = [
# ALL YOUR APPS
'drf_turbo',
]
and then register our openapi AutoSchema with DRF.
REST_FRAMEWORK = {
# YOUR SETTINGS
'DEFAULT_SCHEMA_CLASS': 'drf_turbo.openapi.AutoSchema',
}
and finally add these lines in urls.py
from django.views.generic import TemplateView
from rest_framework.schemas import get_schema_view as schema_view
from drf_turbo.openapi import SchemaGenerator
urlpatterns = [
# YOUR PATTERNS
path('openapi', schema_view(
title="Your Project",
description="API for all things …",
version="1.0.0",
generator_class=SchemaGenerator,
public=True,
), name='openapi-schema'),
path('docs/', TemplateView.as_view(
template_name='docs.html',
extra_context={'schema_url':'openapi-schema'}
), name='swagger-ui'),
]
Now go to http://127.0.0.1:8000/docs
Credits
This package was created with Cookiecutter and the audreyr/cookiecutter-pypackage project template.