Audoma — automatic documentation maker

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 serializersclass 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

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
iteo

iteo

iteo is an international digital product studio founded in Poland, that helps businesses benefit from technology better. Visit us on www.iteo.com