See the generated docs for this example here, and the code here.
Request Body ~~~~~~~~~~~~ Document request body with ``request_schema`` or ``See the generated docs for this example here, and the code here.
By default, the media type documented is ``application/json`` if a pydantic model or a DRF serializer is passed as the value for ``request_schema``. To customize for multiple media types or to change the default media type, pass in a dictionary with the string media type value as the key and the schema (pydantic model / DRF serializer) as the value. .. code:: python ... class ToyUploadImageSchema(Schema): """Example values are not available for application/octet-stream media types.""" ... __root__ : bytes class UploadImageAPI(APIView): summary = "Uploads an image" path_params = ToyIdSchema query_params = ToyMetaDataSchema request_schema = { "application/octet-stream": ToyUploadImageSchema } response_schema = ToyUploadImageSuccessSchema def post(self, request, toyId: int): return Response({}) .. raw:: htmlSee the generated docs for this example here, and the code here.
Response Objects ---------------- Single Response ~~~~~~~~~~~~~~~ Response objects are documented using the attribute ``response_schema`` or ``See the generated docs for this example here, and the code here.
Multiple Responses ~~~~~~~~~~~~~~~~~~ For multiple responses, or to change the default response, you may pass in a dictionary to ``response_schema`` with HTTP status code as a key and a pydantic model or DRF serializer as the value. To customize the content type of the response, pass in a string tuple containing the status code and the content type as the key. .. code:: python class Login(APIView): summary = "Logs user into the system" query_params = LoginRequestSchema response_schema = { "200":LoginSuccessSchema, "400":LoginErrorSchema, ("403", "text/plain"): ForbiddenSchema } def get(self, request): ... return Response({}) .. raw:: htmlSee the generated docs for this example here, and the code here.
Response Headers ~~~~~~~~~~~~~~~~ To document response headers, add ``headers`` to the ``Config`` class in the pydantic model schema. The value should be a dictionary with the header value as key and a Header object as the value. Refer the OpenAPI 3 docs for more information on the Header object specification. .. code:: python from pydantic import BaseModel as Schema class LoginSuccessSchema(Schema): __root__ : str class Config: headers = { "X-Rate-Limit":{ "description":"calls per hour allowed by the user", "type":"integer", "schema":{ "type":"integer" } }, "X-Expires-After":{ "description":"date in UTC when token expires", "type":"string", "schema":{ "type":"strings" } } } .. raw:: htmlSee the generated docs for this example here, and the code here.
Schema Examples ---------------- To set examples for the schemas, define a classmethod ``example`` in the pydantic Schema model that returns an instance of itself with specific values. Defining examples this way has the added benefit of your examples being validated by the schema itself. Examples defined this way only apply to documenting request bodies and responses i.e. ``request_schema`` and ``response_schema``. .. code:: python from pydantic import BaseModel as Schema from rest_framework.views import APIView class UserSchema(Schema): """A User object""" id : int username : str firstName : str lastName : str email : str password : str phone : str userStatus : int @classmethod def example(cls): return cls( id=10, username="theUser", firstName="John", lastName="James", email="john@email.com", password="12345", phone="12345", userStatus=4 ) class CreateUserAPI(APIView): """This can only be done by the logged in user.""" summary = "Create user" request_schema = UserSchema response_schema = UserSchema def post(self, request): ... return Response({}) .. raw:: htmlSee the generated docs for this example here, and the code here.
Defining examples for path, query, header and cookie parameters are done within the ``Field`` itself. For example: .. code:: python from pydantic import BaseModel as Schema, Field class ArticleYearSchema(Schema): year : int = Field(required=True, example="2009") Advanced Schemas ---------------- All ``Schema`` objects here are simply aliases of the pydantic ``BaseModel``. So all features of a pydantic model object can be utilized to define your schemas. Nested Schemas ~~~~~~~~~~~~~~ To document nested schemas, you may use pydantic models as field types. This allows complex schemas to be managed easily and its components reusable. .. code:: python from pydantic import BaseModel as Schema, Field from typing import List, Optional from enum import Enum class Status(str, Enum): available = 'available' pending = 'pending' sold = 'sold' class Category(Schema): """Toy Category""" id : int name : str class Tag(Schema): """Toy Tag""" id : int name : str class ToySchema(Schema): """Toy Schema""" id : Optional[int] name : str category : Optional[Category] photoUrls : List[str] tags : Optional[List[Tag]] status : Optional[Status] = Status.available ... .. raw:: htmlSee the generated docs for this example here, and the code here.
Non-object Schemas ~~~~~~~~~~~~~~~~~~ By default, ``Schema`` objects will be documented as a JSON *object* i.e. an unordered set of name/value pairs. To change this, for example, if your API returns an array instead, change the ``__root__`` value of the ``Schema``. Following from the example above: .. code:: python class ToyArraySchema(Schema): """An array of Toys""" __root__: List[ToySchema] ... class FindToyByStatusAPI(APIView): """ Find Toys by status""" summary = "Find Toys by status" query_params = StatusSchema response_schema = { "200":ToyArraySchema, "400":InvalidToySchema } def get(self, request): return Response({}) .. raw:: htmlSee the generated docs for this example here, and the code here.
List of Djagger View attributes ------------------------------- The table below lists the attributes that can be defined in a view that will be extracted by Djagger to build the documentation. For class-based views encompassing multiple HTTP methods, the attributes below will apply to ALL HTTP methods. To differentiate the attributes for different HTTP methods, prefix the method name in front of any of the attributes in the table below. For example, instead of declaring the class attribute ``response_schema``, you may declare both ``get_response_schema`` and ``post_response_schema`` to differentiate between GET and POST responses. See example below. The available HTTP method names for the prefix are ``get``, ``post``, ``patch``, ``delete``, ``put``, ``options``, ``head``, ``trace``. .. NOTE:: **Hierarchy of Specificity** - A more specific declaration of a Djagger view attribute will override a less specific one. For example, having both ``summary`` and ``post_summary`` attributes will result in the POST endpoint taking on the value of ``post_summary`` while the other endpoints will take on the summary value of ``summary`` in the documentation. +------------------+---------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Attribute | Type | Description | +==================+=======================================================================================+=======================================================================================================================================================================================================================================================================================================================+ | path_params | pydantic.main.ModelMetaclass | rest_framework.serializers.SerializerMetaclass | Schema for the parameter values that are part of the URL E.g. /article/See the generated docs for this example here, and the code here.
Function-based Views -------------------- Djagger supports documenting function-based views (FBV). For FBVs, add the decorator ``@schema`` to the view function. The decorator takes in a required ``methods`` list of string HTTP methods that indicate the HTTP methods handled by the view. Arguments can be passed based on the List of Djagger View attributes into the decorator, these arguments work in the same manner as the class attributes for class-based views. .. code:: python from pydantic import BaseModel as Schema from djagger.decorators import schema from typing import List class AuthorSchema(Schema): first_name : str last_name : str class AuthorListSchema(Schema): authors : List[AuthorSchema] @schema( methods=['GET', 'POST'], get_summary="List Authors", get_response_schema=AuthorListSchema, post_summary="Create Author", post_request_schema=AuthorSchema, post_response_schema=AuthorSchema, ) def author_api(request): """API to create an author or list all authors""" if request.method == 'get': ... return Response({}) if request.method == 'post': ... return Response({}) .. raw:: htmlSee the generated docs for this example here, and the code here.
Using Serializers ----------------- Wherever a pydantic model is used as a schema e.g. in documenting responses or request parameters, a Django REST Framework (DRF) Serializer object can also be used as an alternative. Under the hood, Djagger converts the serializer class (``rest_framework.serializers.SerializerMetaclass``) to a valid pydantic model (``pydantic.main.ModelMetaclass``). For example: .. code:: python from rest_framework import serializers from rest_framework.views import APIView from .schemas import ArticleDetailSchema class ArticleUpdateSerializer(serializers.Serializer): pk = serializers.IntegerField(help_text="Primary key of article to update") title = serializers.CharField(required=False) content = serializers.CharField(required=False) class ArticleUpdateAPI(APIView): request_schema = ArticleUpdateSerializer response_schema = ArticleDetailSchema def put(self, request): ... return Response({}) .. raw:: htmlSee the generated docs for this example here, and the code here.
Generic Views ------------- For generic views, if a ``serializer_class`` attribute is defined for the generic view, Djagger will treat it as a ``response_schema`` attribute. Setting ``response_schema`` directly in the generic view will override this. Other than that, documenting generic views is the same as a regular class-based view. .. code:: python class CategoryList(generics.ListCreateAPIView): """Example Generic View Documentation""" serializer_class = CategorySerializer(many=True) get_summary = "Category List" post_summary = "Category List Create" def list(self, request): ... return Response({}) def create(self, request): ... return Response({}) .. raw:: htmlSee the generated docs for this example here, and the code here.
Viewsets -------- Documenting DRF viewsets is supported. To document each viewset action e.g. ``list()``, ``create()``, decorate the action method with the ``@schema`` decorator similar to how a function-based view is documented in Djagger. For viewsets, the parent viewset class attributes can also be used for documenting the actions, and they will apply to all actions under the viewset. The ``@schema`` values will take priority over the parent class djagger atributes. .. code:: python class CategoryViewset(viewsets.ViewSet): """Example Viewset documentation""" response_schema = CategoryListSchema @schema( methods=['GET'], summary="List Categories", ) def list(self, request): ... return Response({}) @schema( methods=['GET'], summary="Get Category", response_schema=CategorySerializer, ) def retrieve(self, retrieve): ... return Response({}) .. raw:: htmlSee the generated docs for this example here, and the code here.
Tags ---- You can group APIs with tags. Tags are an OpenAPi 3 schema object and they can be declared using the ``tags`` djagger attribute. ``tags`` should be a list of string tag names for which the operation should be assigned to. By default, djagger will create tags for each Django app included in the project, unless overwritten in the ``DJAGGER_DOCUMENT`` settings configuration. In your views, if ``tags`` attribute is not set, the API will be automatically assigned the tag name of the Django app in which it resides. .. NOTE:: The http method prefix is also applicable to the ``tags`` attribute. So a view with ``tags=['MyApp']`` and ``post_tags=['Admin']`` will have POST operations take on the ``Admin`` tag while other operations assigned the ``MyApp`` tag. **Example** .. code:: python class MyView(APIView): tags = ['MyApp'] post_tags = ['Admin'] def get(self, request): # Assigned to 'MyApp' tag ... return Response({}) def post(self, request): # Assigned to 'Admin' tag ... return Response({}) Defining tags at the document level ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Define the tags at the document level in with the ``DJAGGER_DOCUMENT`` settings in ``settings.py``. ``tags`` key should be a list of tag dictionaries of type ``Dict[str,str]`` containing ``name`` and ``description`` keys. **Example** .. code:: python # settings.py DJAGGER_DOCUMENT = { ... "tags" : [ { 'name':'Tag1', 'description': 'Example Tag'}, { 'name':'Tag2', 'description': 'Example Tag'}, ], ... } Defining document-level tags in this way overrides the default tags created for each django app. Document Generation ------------------- To see the generated documentation, ensure that the Djagger URLs are installed correctly. See :ref:`getting_started:Getting Started`. Configuring the built-in Djagger views ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Configuration for the built-in document view is managed in ``settings.py`` via the dictionary parameter ``DJAGGER_DOCUMENT``. See table below for the valid dict keys in ``settings.DJAGGER_DOCUMENT``. +-------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Keys | Type | Description | +===================+============+============================================================================================================================================================================================================================================+ | app_names | List[str] | Required list of string names for the Django apps to be documented. Apps not included in this list will not be documented. | +-------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | tags | List[dict] | List of dictionary objects with name and description fields representing the OpenAPI Tag object. By default, the list of declared app names in DJAGGER_APP_NAMES will be created as the OpenAPI Tags, unless overridden by this variable. | +-------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | openapi | str | OpenAPI 3 version. Defaults to 3.0.0. | +-------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | version | str | Project version. Defaults to 1.0.0. | +-------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | servers | List[dict] | List of dictionary Server objects. Each object is a dictionary with url and description string fields. | +-------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | security | List[dict] | List of dictionary Security objects. | +-------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | title | str | Title of documentation. | +-------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | description | str | Description of documentation. | +-------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | terms_of_service | str | Terms of service. | +-------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | contact_name | str | Contact name. | +-------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | contact_email | str | Contact email. | +-------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | contact_url | str | Contact URL. | +-------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | license_name | str | Name of license | +-------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | license_url | str | URL of license | +-------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | kwargs | | Additional key/value pairs to be appended to the generated JSON document. Usually used for ``x-`` fields for specific document generating clients. | +-------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ **Example Djagger Document settings** .. code:: python # settings.py DJAGGER_DOCUMENT = { "version" : "1.0.0", "title" : "Djagger Toy Store", "description" : """This is a sample OpenAPI 3.0 schema generated from a Django project using Djagger. View the Django project that generated this document on Github: https://github.com/royhzq/djagger-example. """, "license_name" : "MIT", "app_names" : [ 'Toy', 'Store', 'User', 'Blog'], "tags" : [ { 'name':'Toy', 'description': 'Toy App'}, { 'name':'Store', 'description': 'Store App'}, { 'name':'User', 'description': 'User App'}, { 'name':'Blog', 'description': 'Blog App'}, ], "x-tagGroups" : [ { 'name':'GENERAL', 'tags': ['Toy', 'Store', 'Blog']}, { 'name':'USER MANAGEMENT', 'tags': ['User']} ], "servers" : [ { "url":"https://example.org", "description":"Production API server" }, { "url":"https://staging.example.org", "description":"Staging API server" } ], } .. raw:: htmlSee the generated docs for this example here, and the code here.
Customized documentation views ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To create your own documentation view, generate the document via Djagger's ``Document`` class. See example below: .. code:: python from djagger.openapi import Document from django.http import JsonResponse def custom_doc_view(request): """ Custom documentation View for auto generated OpenAPI JSON document """ doc_settings = { "version" : "1.0.0", "title" : "Djagger Toy Store", "description" : """This is a sample OpenAPI 3.0 schema generated from a Django project using Djagger. View the Django project that generated this document on Github: https://github.com/royhzq/djagger-example. """, "license_name" : "MIT", "app_names" : [ 'Toy', 'Store', 'User', 'Blog'], "tags" : [ { 'name':'Toy', 'description': 'Toy App'}, { 'name':'Store', 'description': 'Store App'}, { 'name':'User', 'description': 'User App'}, { 'name':'Blog', 'description': 'Blog App'}, ], "x-tagGroups" : [ { 'name':'GENERAL', 'tags': ['Toy', 'Store', 'Blog']}, { 'name':'USER MANAGEMENT', 'tags': ['User']} ], "servers" : [ { "url":"https://example.org", "description":"Production API server" }, { "url":"https://staging.example.org", "description":"Staging API server" } ], } document : dict = Document.generate(**doc_settings) response = JsonResponse(document) response['Cache-Control'] = "no-cache, no-store, must-revalidate" return response The ``custom_doc_view`` view in the example returns a JSON response containing the OpenAPI 3 compliant JSON schema. You may then use your preferred documentation client generator to consume the JSON schema from the view to generate your desired documentation. Security & Authentication ------------------------- Documenting security schemes for broadly follows the OpenAPI format whereby the Security Scheme objects will need to be defined under the Components Object in the root of the OpenAPI document. What this means in Djagger will be that the Security Scheme object will be defined in the ``DJAGGER_DOCUMENT`` parameter in ``settings.py``. For example: .. code:: python # settings.py DJAGGER_DOCUMENT = { ... "components": { "securitySchemes": { "http": { "type": "http", "scheme": "basic" }, "jwt": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", }, } } ... } Under the ``components`` key in ``DJAGGER_DOCUMENT``, a ``securitySchemes`` key should be present that holds a dictionary. In this dictionary, you can declare the names of your security schemes e.g., ``http``, or ``jwt`` as they keys and the values will be dictionary objects corresponding to the Security Scheme object in the OpenAPI specification. .. raw:: htmlFor more details on declaring security schmee objects, please see the OpenAPI specification documentation here here
Next, to document your endpoints with the respective security schemes as declared in ``DJAGGER_DOCUMENT``, declare a ``security`` attribute in your API View which is of type ``List[Dict[str, List[str]]]``. This verbose way of documenting an endpoint's authentication schemes is to be consistent with the OpenAPI specification. For example: .. code:: python class PlaceOrderAPI(APIView): summary = "Place an order for a toy" ... security = [ {"http": []} ] def post(self, request): ... return Response({}) .. raw:: htmlFor more details on the security requirement object, please see the OpenAPI specification documentation here here
Global attribute prefix ----------------------- In the event that certain djagger attributes come into conflict with those from other packages when used together in the same view, you can set a global prefix for all djagger attributes to overcome this. For example, a global prefix of ``dj_`` will mean that all djagger attributes will need to be prefixed as such for all class-based views as well as the parameters of the ``@schema`` function-based view decorator. I.e. Instead of ``get_summary``, the attribute will be ``dj_get_summary``. To set the prefix, add the following in ``settings.py`` .. code:: python DJAGGER_CONFIG = { "global_prefix" = "dj_" } With the prefix, an example of a documented view will be: .. code:: python # Class-based view class TestView(APIView): dj_get_summary="Test View" dj_request_schema=... dj_response_schema=... # Function-based view @schema( dj_get_summary="Test View" dj_request_schema=... dj_response_schema=... ) def fbv(request): ...