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":"56ca3ca5-5402-4b7c-b9a4-3b9900489aab"}
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!