Oct 11, 2022, Web

Audoma – automatic documentation maker

Mateusz Tytoń Python developer
Keeping the documents in folders
Documenting API may be a fuss, especially if you want your documentation to be as accurate as possible. Nobody likes writing docs manually. We all use tools to automate the work, but often lose some information in the process. Sometimes even the crucial ones.

That’s the problem that Audoma is trying to solve, creating the most accurate documentation possible. Besides creating docs, Audoma also ensures that documentation is consistent with what’s defined in the code, trying to avoid situations where it differs from implemented functionality.

About Audoma

The main purpose of Audoma is to generate more accurate docs.

It has been built on the top of drf-spectacular, in fact extending many of its functionalities.

 

So, how does Audoma make life easier?

If you use drf-spectacular to add field example you’ll have to do this:

from drf_spectacular.utils import extend_schema_field
from rest_framework import fields
from rest_framework import serializers

@extend_schema_field(field={"example": random.randint(18, 150)})
class IntegerField(fields.IntegerField):
	pass

@extend_schema_field(field={"example": "John"})
class NameField(fields.CharField):
	pass

class PersonSerializer(serializers.Serializer):
	name = fields.CharField(max_length=255)
	age = IntegerField()

If you are using Audoma, you can simply do this:

from audoma.drf import serializers

class PersonSerializer(serializers.Serializer):
	name = serializers.CharField(max_length=255, example="John")
	age = serializers.IntegerField(min_value=18, max_value=150)

Setup

Audoma may be installed easily by using pip:

pip install audoma

After installation you have to modify your projects settings file:

REST_FRAMEWORK = {
	# YOUR SETTINGS
	'DEFAULT_SCHEMA_CLASS': 'audoma.drf.openapi.AudomaAutoSchema',
}
SCHEMA_PATTERN_PREFIX = 'api'
SPECTACULAR_SETTINGS = {
	'PREPROCESSING_HOOKS':[
         'audoma.hooks.preprocess_include_path_format'
      ],
	# OTHER SETTINGS
}

That’s all, you can start using Audoma!

Example features

Audoma extends and automates many drf-spectacular behaviors.

As mentioned above, defining examples for fields in Audoma is much easier.

 

It also provides new fields that allow the definition of custom examples.

If you want to create a phone number field with example without Audoma this would require installing the proper package and then:

from django.db import models
from phonenumber_field.modelfields import PhoneNumberField
from drf_spectacular.utils import extend_schema_field
@extend_schema_field(field={
	"example": "+123456789"
})
class CustomPhoneNumberField(PhoneNumberField):
	pass

class Customer(models.Model):
    	name = models.CharField(max_length=255)
    	phone_number = CustomPhoneNumberField()

Audoma has this field built-in and provides two easy ways of defining examples:

from audoma.django import models
class Customer(models.Model):
	name = models.CharField(max_length=255)
	phone_number = models.PhoneNumberField(region="PL")
	mobile_phone = models.PhoneNumberField(example="123456789")

If you pass example=”123456789″, the value would be shown as an example.

But if you pass region=”PL”, an example will be generated for the passed region, example will match the passed region phone number pattern.

Another great thing is that Audoma allows you to have multiple serializer classes defined on your Viewset:

from rest_framework.decorators import action
 from rest_framework.response import Response
 from audoma.drf import viewsets
 from audoma.drf import mixins
 from example_app.serializers import (
 	MyListSerializer,
 	MySerializer,
 	MyCreateSerializer
 )

 class MyViewSet(
 	mixins.ActionModelMixin,
 	mixins.ListModelMixin,
 	viewsets.GenericViewSet
 ):
	serializer_class = MySerializer
	get_new_action_result_serializer_class = MyListSerializer
	post_new_action_result_serializer_class = MySerializer
	post_new_action_collect_serializer_class = MyCreateSerializer
	@action(detail=True, methods=["post", "get"])
	def new_action(self, request, *args, **kwargs):
    		if request.method == "POST":
        		serializer = self.get_serializer(data=request.data)
        		serializer.is_valid(raise_exceptio n=True)
        		serializer.save()
        		instance = serializer.instance
    		else:
        		instance = self.get_object()
    		response_serializer = self.get_result_serializer(instance=instance)
    		return Response(response_serializer.data, status_code=201)

As you may see in the example above Audoma allows your Viewset action to have two different serializers for each served HTTP method. One for collecting data, the second for creating the response. With this on your Viewset you may also have the default serializer_class variable defined. To know more about audoma serializer class definitions see: Docs

More than that you may define those serializers per action using a custom action decorator!

class CarViewSet(
 	mixins.ActionModelMixin,
 	mixins.CreateModelMixin,
 	mixins.RetrieveModelMixin,
 	mixins.ListModelMixin,
 	viewsets.GenericViewSet,
 ):
 	@audoma_action(
     	detail=False,
     	methods=["get", "post"]
     	collectors=CarCreateRateSerializer,
     	results={
         	"post":{201: CarRateSerializer},
         	"get":{200: CarDetailsSerializer}
     	},
     	errors=[CustomCarException]
 	)
 	def rate(self, request, *args, **kwargs):
     		if settings.RATE_AVAILABLE:
         		return None, 204
     		if request.method == "POST":
         		collect_serializer = kwargs.pop("collect_serializer")
         		instance = collect_serializer.save()
         		return instance. 201
     		else:
         		instance = car.objects.get(pk=pk)
         		if not instance:
           	 	raise CustomCarException
         		return instance, 200

Using this action you may define different serializers for data collection and response generation.
What’s great is that all of the serializers defined in audoma_action will be documented properly. 

If you want to learn more about audoma, please visit our repo, or simply read the documentation.

Contribution

Audoma accepts any type of contribution to the project!
All new feature or fix proposals should be created via pull request or issue in our repository.

Pull requests

We advise sticking to the below rules:

  • Test your changes before creating pull requests – your code has to pass the whole test suite to get merged. The best way will be by launching docker tests, included with the project

  • Use linters included in Audoma

  • Write your own tests for your changes

  • If your changes are not trivial consider creating an issue first with changes proposals to get some early feedback

  • If you are introducing a new feature, please add an example for this feature in Audoma example application

Issues

To each submitted issue, please include:

  • Brief description of the issue

  • Stack trace if there has been an exception raised

  • Code that caused the issue

  • audoma/drf-spectacular/Django/DRF versions

Share