Micro-framework out of Django#
Single file Django#
You don’t need microframeworks to build small APIs, because Django is a microframework itself. With the only difference: it scales!
1import secrets
2import sys
3import uuid
4
5import pydantic
6from django.conf import settings
7from django.core.management import execute_from_command_line
8from django.urls import include
9
10from dmr import Body, Controller
11from dmr.openapi import build_schema
12from dmr.openapi.views import OpenAPIJsonView, SwaggerView
13from dmr.plugins.pydantic import PydanticSerializer
14from dmr.routing import Router, path
15
16if not settings.configured:
17 settings.configure(
18 ROOT_URLCONF=__name__,
19 ALLOWED_HOSTS='*',
20 DEBUG=True,
21 INSTALLED_APPS=['dmr', 'django.contrib.staticfiles'],
22 STATIC_URL='/static/',
23 STATICFILES_FINDERS=[
24 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
25 ],
26 TEMPLATES=[
27 {
28 'APP_DIRS': True,
29 'BACKEND': 'django.template.backends.django.DjangoTemplates',
30 },
31 ],
32 # Secret key for tests, will be new on each run,
33 # in production it must be the same token, kept in secret:
34 SECRET_KEY=secrets.token_hex(),
35 )
36
37
38class UserCreateModel(pydantic.BaseModel):
39 email: str
40
41
42class UserResponseModel(UserCreateModel):
43 uid: uuid.UUID
44
45
46class UserController(Controller[PydanticSerializer]):
47 async def post(
48 self,
49 parsed_body: Body[UserCreateModel],
50 ) -> UserResponseModel:
51 return UserResponseModel(uid=uuid.uuid4(), email=parsed_body.email)
52
53
54router = Router(
55 'api/',
56 [
57 path('user/', UserController.as_view(), name='users'),
58 ],
59)
60schema = build_schema(router)
61
62urlpatterns = [
63 path(router.prefix, include((router.urls, 'your_app'), namespace='api')),
64 path('docs/openapi.json/', OpenAPIJsonView.as_view(schema), name='openapi'),
65 path('docs/swagger/', SwaggerView.as_view(schema), name='swagger'),
66]
67
68if __name__ == '__main__':
69 # Use `python THIS_FILE_NAME.py runserver` to run the example.
70 # Then visit `http://localhost:8000/docs/swagger` to view the docs.
71 execute_from_command_line(sys.argv)
Run result
$ curl http://127.0.0.1:8000/api/user/ -X POST -d '{"email": "djangomodernrest@wemake.services"}' -H 'Content-Type: application/json'
{"email":"djangomodernrest@wemake.services","uid":"322484a6-0820-4f4e-b2ae-b17f8cc59d6c"}
OpenAPI Schema
Preview openapi.json
{
"components": {
"schemas": {
"ErrorDetail": {
"description": "Base schema for error details description.",
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "integer"
},
{
"type": "string"
}
]
},
"title": "Loc",
"type": "array"
},
"msg": {
"title": "Msg",
"type": "string"
},
"type": {
"title": "Type",
"type": "string"
}
},
"required": [
"msg"
],
"title": "ErrorDetail",
"type": "object"
},
"ErrorModel": {
"description": "Default error response schema.\n\nCan be customized.\nSee :ref:`customizing-error-messages` for more details.",
"properties": {
"detail": {
"items": {
"$ref": "#/components/schemas/ErrorDetail"
},
"title": "Detail",
"type": "array"
}
},
"required": [
"detail"
],
"title": "ErrorModel",
"type": "object"
},
"UserCreateModel": {
"properties": {
"email": {
"title": "Email",
"type": "string"
}
},
"required": [
"email"
],
"title": "UserCreateModel",
"type": "object"
},
"UserResponseModel": {
"properties": {
"email": {
"title": "Email",
"type": "string"
},
"uid": {
"format": "uuid",
"title": "Uid",
"type": "string"
}
},
"required": [
"email",
"uid"
],
"title": "UserResponseModel",
"type": "object"
}
},
"securitySchemes": {}
},
"info": {
"title": "Django Modern Rest",
"version": "0.1.0"
},
"openapi": "3.2.0",
"paths": {
"/api/user/": {
"post": {
"deprecated": false,
"operationId": "postUsercontrollerApiUser",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UserCreateModel"
}
}
},
"required": true
},
"responses": {
"201": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UserResponseModel"
}
}
},
"description": "Created"
},
"400": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorModel"
}
}
},
"description": "Raised when request components cannot be parsed"
},
"406": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorModel"
}
}
},
"description": "Raised when provided `Accept` header cannot be satisfied"
},
"422": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorModel"
}
}
},
"description": "Raised when returned response does not match the response schema"
}
}
}
}
}
}
Scaling it for real projects#
However, all projects tend to grow while they are alive. Microframeworks handle the scale poorly, because they are not designed for this task.
We recommend using https://github.com/wemake-services/wemake-django-template to set up production ready Django boilerplate code with all the best practices.
It can handle any scale!