Customized features and environment for building a Django REST framework app.
- Python 3.8
- Django 3.0
- djangorestframework (Django REST framework)
Optional packages required for additional features.
- PyJWT
TokenAuthentication - channels
NullURLRouter,NullConsumer - sentry-sdk
sentry_report(when Sentry reports enabled) - numpy
rest_base.utils.random
pip install django-rest-baseINSTALLED_APPS = [
...,
'rest_base',
]REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'rest_base.errors.exception_handler',
}from rest_base.errors import Error
MyAppError = Error('my_app') # class
MyError = MyAppError('My', 'Error') # class
MyErrorInstance = MyError(detail='caution: this is instance!') # instance (when *args and code not provided)from my_app.errors import MyError, MyErrorInstance
def my_view(request):
raise MyError(detail='Something went wrong :(')
def another_view1(request):
raise MyError # raise without parameters is also permitted
def another_view2(request):
raise MyErrorInstance() # __call__ should be used to raise instancerest_base.errors.exception_handler is similar with REST framework's default handler, but provides advanced error format.
In Django REST base environment, every exception from views inherits rest_base.errors.Error. Even original REST framework's exceptions and unhandled exceptions will be converted to Error.
Those Errors will generate Response which has following error format.
{
"error": {
"code": "My::Error",
"detail": "Something went wrong :(",
"traceback": "(traceback)"
}
}detail and traceback are optional, and traceback will be included automatically when Django's DEBUG mode is enabled.
from django.urls import re_path
from rest_base.urls import method_branch
from . import views
urlpatterns = [
re_path(r'^my_endpoint/?$', method_branch(GET=views.my_view, POST=views.another_view)),
]rest_base.urls.method_branch branch the requests by it's method—GET, POST, PUT, DELETE.
It also supports @rest_framework.decorators.authentication_classes and @rest_framework.decorators.permission_classes for each view.
from rest_base.models import BaseModel, BaseUser, BaseToken, semaphore
@semaphore(block=True)
class MyModel(BaseModel):
field = ...
class MyUser(BaseUser):
field = ...
class MyToken(BaseToken):
field = ...By replacing the original Django models.Model with rest_base.models.BaseModel, several customized features can be used.
created,last_modifiedfieldsobjects.update_or_createupdates instance only if original attributes anddefaultsare not same.bulk_manager- Supports deadlock free following methods. (PostgreSQL ONLY)
update,bulk_update,delete
PredefinedDefault(See Fields for more)related_namemust be provided when field's type isForeignKey,OneToOneFieldorManyToManyField.
You can use customized BaseUser and JWT based BaseToken by inherit it.
See Authentication for more information about JWT authentication.
from django.db import models
from rest_base.fields import UniqueRandomPositiveInt32
from rest_base.models import BaseModel
class MyModel(BaseModel):
unique_field = models.IntegerField(unique=True, default=UniqueRandomPositiveInt32)If your model inherits rest_base.models.BaseModel, you can set the default value as
UniqueRandomPositiveInt32UniqueRandomPositiveInt52UniqueRandomPositiveInt64UniqueRandomChar(unique random url-safe characters)
Then default value will be replaced with unique random value in the run-time.
from django.contrib import admin
from rest_base.admin import model_admin
from .models import *
admin.site.register(*model_admin(MyModel))You can easily register you models to Django admin page by using rest_base.admin.model_admin.
It registers all of the model's fields and supports link to ForeignKey, OneToOneField and ManyToManyField on Django admin.
pip install django-rest-base[jwt]from rest_base.models import BaseToken
class Token(BaseToken):
passREST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_base.authentication.CsrfExemptSessionAuthentication',
'rest_base.authentication.TokenAuthentication',
),
}
REST_BASE = {
'AUTHENTICATION_MODEL': 'my_app.models.Token',
}from rest_framework.decorators import permission_classes
from rest_framework.permissions import IsAuthenticated
@permission_classes((IsAuthenticated,))
def my_view(request):
...Each Token which inherits BaseToken has public_key and secret_key.
You can make bearer using following format.
// Header
{
"alg": "HS256",
"typ": "JWT",
"public_key": "public_key",
"nonce": 31
}
// Payload
{
"query": {
"key": "value"
}
}Then set the HTTP Authorization header to
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsInB1YmxpY19rZXkiOiJwdWJsaWNfa2V5Iiwibm9uY2UiOjMxfQ.eyJxdWVyeSI6eyJrZXkiOiJ2YWx1ZSJ9fQ.q1849tjsSspbtyBYHxsmS98FLUIG2W97aj_gaCxWGlg
Remember that nonce must be a positive int32 value which increases for each request.
pip install django-rest-base[sentry]REST_BASE = {
'SENTRY_HOST': 'https://<key>@sentry.io/<project>',
'SENTRY_VERBOSE': False, # report handled exceptions, default False
}If sentry-sdk installed, SENTRY_HOST defined and rest_base.errors.exception_handler configured correctly,
every unhandled exception from view will be reported to the Sentry.
Handled exceptions also reported if you set SENTRY_VERBOSE to True.
pip install django-rest-base[channels]from django.urls import path
from rest_base.channels.routing import NullURLRouter
websocket_urlpatterns = [
path('app/', NullURLRouter(my_app.routing.websocket_urlpatterns)),
]- By default, startapp command will use template in
rest_base/conf/app_templatewhich contains additional code for Django REST base - .env can be loaded by
from rest_base.utils import dotenv
dotenv.load('path/to/.env')- You can dump/load predefined model instances by using
python manage.py dump my_app.Model
python manage.py load my_app.Model