from audoma.drf import viewsets
from audoma.drf import mixins
from example_app.serializers import (
MyCollectSerializer,
MyResultSerializer
)
class MyViewSet(
mixins.ActionModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet
):
common_collect_serializer_class = MyCollectSerializer
common_result_serializer_class = MyResultSerializer
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 (
MyCreateSerializer,
MyCustomActionSerializer
)
class MyViewSet(
mixins.ActionModelMixin,
mixins.CreateModelMixin,
viewsets.GenericViewSet
)::
create_serializer_class = MyCreateSerializer
custom_action_serializer_class = MyCustomActionSerializer
@action(detail=True, methods=["post"])
def custom_action(self, request):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.instance, status_code=200)
from audoma.drf import viewsets
from audoma.drf import mixins
from example_app.serializers import (
MyCreateSerializer,
MyCustomActionSerializer
)
class MyViewSet(
mixins.ActionModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet
):
get_list_serializer_class = MyListSerializer
post_list_serializer_class = MyBulkCreateSerializer
from rest_framework.decorators import action
from rest_framework.response import Response
from audoma.drf import viewsets
from example_app.serializers import (
MyCreateSerializer,
MyCustomActionSerializer
)
class MyViewSet(
mixins.ActionModelMixin,
viewsets.GenericViewSet
):
custom_action_collect_serializer = MyModelCreateSerializer
custom_action_result_serializer = MyModelSerializer
@action(detail=True, methods=["post"])
def custom_action(self, request):
serializer = self.get_serializer(data=request.data, serializer_type="collect")
serializer.is_valid(raise_exception=True)
serializer.save()
response_serializer = self.get_result_serializer(instance=serializer.instance)
return Response(response_serializer.data, status_code=201)
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
):
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_type="collect")
serializer.is_valid(raise_exception=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)
from rest_framework.decorators import action
from rest_framework.response import Response
from audoma.drf import viewsets
from example_app.serializers import (
MySerachCollectSerializer,
MySearchResultSerializer,
MyCountCreateSerializer,
MyCountUpdateSerializer,
MyCountResultSerializer,
MyDefaultSerializer
)
from example_app.models import (
MyModel,
CountModel
)
class MyViewSet(
mixins.ActionModelMixin,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet,
):
queryset = MyModel.objects.all()
post_search_collect_serializer_class = MySerachCollectSerializer
post_search_result_serializer_class = MySearchResultSerializer
post_count_collect_serializer_class = MyCountCreateSerializer
put_count_collect_serializer_class = MyCountUpdateSerializer
count_result_serializer_class = MyCountResultSerializer
serializer_class = MyDefaultSerializer
def get_object(self, pk=None):
return self.querset.get(pk=pk)
@action(detail=False, methods=["post"])
def search(self, request):
serializer = self.get_serializer(data=request.data, serializer_type="collect")
serializer.is_valid(raise_exception=True)
serializer.save()
result_serializer = self.get_result_serializer(instance=serializer.instance)
return Response(result_serializer.data, status=201)
@action(detail=True, methods=["post", "get", "put"])
def count(self, request, *args, **kwargs):
code = 200
if request.method != "GET":
serializer = self.get_serializer(data=request.data, serializer_type="collect")
serializer.is_valid(raise_exception=True)
serializer.save()
instance = serializer.instance
code = 201 if request.method == "POST"
else:
instance = CountModel.objects.get_count(slug=kwargs.pop("slug"))
result_serializer = self.get_result_serializer(instance=instance)
return Response(result_serializer.data, status=code)
...
search_collect_serializer_class = MySerachCollectSerializer
search_result_serializer_class = MySearchResultSerializer
...
...
post_count_collect_serializer_class = MyCountCreateSerializer
put_count_collect_serializer_class = MyCountUpdateSerializer
count_serializer_class = MyCountResultSerializer
...
from rest_framework.decorators import action
from rest_framework.response import Response
from audoma.drf import viewsets
from example_app.serializers import (
MySerachCollectSerializer,
MySearchResultSerializer,
MyCountCreateSerializer,
MyCountUpdateSerializer,
MyCountResultSerializer,
MyDefaultSerializer,
MyListSerializer,
MyCreateSerializer
)
from example_app.models import (
MyModel,
CountModel
)
class MyViewSet(
mixins.ActionModelMixin,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet,
):
queryset = MyModel.objects.all()
post_search_collect_serializer_class = MySerachCollectSerializer
post_search_result_serializer_class = MySearchResultSerializer
post_count_collect_serializer_class = MyCountCreateSerializer
put_count_collect_serializer_class = MyCountUpdateSerializer
count_result_serializer_class = MyCountResultSerializer
list_serializer_class = MyListSerializer
create_serializer_class = MyCreateSerializer
serializer_class = MyDefaultSerializer
def get_object(self, pk=None):
return self.querset.get(pk=pk)
@action(detail=False, methods=["post"])
def search(self, request):
serializer = self.get_serializer(data=request.data, serializer_type="collect")
serializer.is_valid(raise_exception=True)
serializer.save()
result_serializer = self.get_result_serializer(instance=serializer.instance)
return Response(result_serializer.data, status=201)
@action(detail=True, methods=["post", "get", "put"])
def count(self, request, *args, **kwargs):
code = 200
if request.method != "GET":
serializer = self.get_serializer(data=request.data, serializer_type="collect")
serializer.is_valid(raise_exception=True)
serializer.save()
instance = serializer.instance
code = 201 if request.method == "POST"
else:
instance = CountModel.objects.get_count(slug=kwargs.pop("slug"))
result_serializer = self.get_result_serializer(instance=instance)
return Response(result_serializer.data, status=code)
- {htp_method}_{action_name}_{type}_serializer_class (type can be result or collect)
- {action_name}_{type}_serializer_class (type can be result or collect)
- {http_method}_{action_name}_serializer_class
- {action_name}_serializer_class
- common_{type}_serializer_class (type can be result or collect)
- serializer_class
By default, in the drf-spectacular viewset permissions were not documented at all. In audoma, permissions are being documented for each viewset separately.
You don't have to define anything extra, this is being handled just out of the box. The only thing it is required is to define permissions on your viewset.
Example:
from rest_framework.decorators import action
from rest_framework.response import Response
from audoma.drf import viewsets
from example_app.serializers import (
MySerachCollectSerializer,
MySearchResultSerializer,
MyCountCreateSerializer,
MyCountUpdateSerializer,
MyCountResultSerializer,
MyDefaultSerializer,
MyListSerializer,
MyCreateSerializer
)
from example_app.permissions import (
AlternatePermission1,
AlternatePermission2,
DetailPermission,
ViewAndDetailPermission,
ViewPermission,
)
from example_app.models import (
MyModel,
CountModel
)
class MyViewSet(
mixins.ActionModelMixin,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet,
):
permission_classes = [
IsAuthenticated,
ViewAndDetailPermission,
DetailPermission,
ViewPermission,
AlternatePermission1 | AlternatePermission2,
]
queryset = MyModel.objects.all()
post_search_collect_serializer_class = MySerachCollectSerializer
post_search_result_serializer_class = MySearchResultSerializer
post_count_collect_serializer_class = MyCountCreateSerializer
put_count_collect_serializer_class = MyCountUpdateSerializer
count_result_serializer_class = MyCountResultSerializer
list_serializer_class = MyListSerializer
create_serializer_class = MyCreateSerializer
serializer_class = MyDefaultSerializer
def get_object(self, pk=None):
return self.querset.get(pk=pk)
@action(detail=False, methods=["post"])
def search(self, request):
serializer = self.get_serializer(data=request.data, serializer_type="collect")
serializer.is_valid(raise_exception=True)
serializer.save()
result_serializer = self.get_result_serializer(instance=serializer.instance)
return Response(result_serializer.data, status=201)
@action(detail=True, methods=["post", "get", "put"])
def count(self, request, *args, **kwargs):
code = 200
if request.method != "GET":
serializer = self.get_serializer(data=request.data, serializer_type="collect")
serializer.is_valid(raise_exception=True)
serializer.save()
instance = serializer.instance
code = 201 if request.method == "POST"
else:
instance = CountModel.objects.get_count(slug=kwargs.pop("slug"))
result_serializer = self.get_result_serializer(instance=instance)
return Response(result_serializer.data, status=code)
from audoma.django.db import models
from audoma.choices import make_choices
class CarModel(models.Model):
CAR_BODY_TYPES = make_choices(
"BODY_TYPES",
(
(1, "SEDAN", "Sedan"),
(2, "COUPE", "Coupe"),
(3, "HATCHBACK", "Hatchback"),
(4, "PICKUP", "Pickup Truck"),
),
)
name = models.CharField(max_length=255)
body_type = models.IntegerField(choices=CAR_BODY_TYPES.get_choices())
engine_size = models.FloatField()
def is_sedan(self):
return self.body_type is BODY_TYPE_CHOICES.SEDAN
from rest_framework.filters import SearchFilter
from audoma.drf import mixins
from audoma.drf import viewsets
from django_filters import rest_framework as df_filters
from example_app.models import CarModel
from example_app.serializers import CarModelSerializer
class CarViewSet(
mixins.ActionModelMixin,
mixins.RetrieveModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet,
):
queryset = CarModel.objects.all()
serializer_class = CarModelSerializer
filter_backends = [SearchFilter, df_filters.DjangoFilterBackend]
filterset_fields = ["body_type"]
search_fields = ["=manufacturer", "name"]
from rest_framework.filters import SearchFilter
from audoma.drf import mixins
from audoma.drf import viewsets
from django_filters import rest_framework as df_filters
from example_app.models import CarModel
from example_app.serializers import CarModelSerializer
class CarFilter(df_filters.FilterSet):
body_type = df_filters.TypedChoiceFilter(
Car.CAR_BODY_TYPES.get_choices(), "body_type",
lookup_expr="exact", field_name="body_type"
)
class Meta:
model = CarModel
fields = [
"body_type",
]
class CarViewSet(
mixins.ActionModelMixin,
mixins.RetrieveModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet,
):
queryset = CarModel.objects.all()
serializer_class = CarModelSerializer
filter_backends = [SearchFilter, df_filters.DjangoFilterBackend]
filterset_class = CarFilter
search_fields = ["=manufacturer", "name"]
- fields - list or a tuple of field names
- message - string message, which will replace the default validator message
- required - boolean which determines if any of the fields must be given
- message_required - a message which will be displayed if one of the fields is required, and none has been passed
from audoma.drf import serializers
from audoma.drf.validators import ExclusiveFieldsValidator
class MutuallyExclusiveExampleSerializer(serializers.Serializer):
class Meta:
validators = [
ExclusiveFieldsValidator(
fields=[
"example_field",
"second_example_field",
]
),
]
example_field = serializers.CharField(required=False)
second_example_field = serializers.CharField(required=False)
from audoma.drf.fields import FloatField
from drf_spectacular.utils import extend_schema_field
@extend_schema_field(
field={
"example": 10.00
}
)
class CustomExampleFloatField(FloatField):
pass
from audoma.drf import mixins
from audoma.drf import viewsets
from app.serializers import (
CarListSerializer,
CarWriteSerializer,
CarDetailsSerializer,
CarCreateRateSerializer,
CarRateSerializer
)
from app.models import (
Car,
CarRate
)
class CarViewSet(
mixins.ActionModelMixin,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet,
):
permission_classes = [
IsAuthenticated,
ViewAndDetailPermission,
DetailPermission,
ViewPermission,
AlternatePermission1 | AlternatePermission2,
]
create_collect_serializer_class = CarWriteSerializer
create_result_serializer_class = CarDetailsSerializer
retrieve_serializer_class = CarDetailsSerializer
list_serializer_class = CarListSerializer
queryset = {}
@audoma_action(
detail=True,
methods=["get", "post"]
collectors=CarCreateRateSerializer,
results=CarRateSerializer,
errors=[CustomCarRateException]
)
def rate(self, request, pk=None, *args, **kwargs):
if request.method == "POST":
collect_serializer = kwargs.pop("collect_serializer")
instance = collect_serializer.save()
else:
instance = CarRate.objects.get_random_car_rate(car_pk=pk)
return instance, 200
from audoma.drf import mixins
from audoma.drf import viewsets
from rest_framework.exceptions import APIException
from app.serializers import (
CarListSerializer,
CarWriteSerializer,
CarDetailsSerializer,
CarCreateRateSerializer,
CarRateSerializer
)
from app.models import (
Car,
CarRate
)
class CustomCarRateException(APIException):
default_detail = "Error during retrieving car rate!"
status_code = 500
class CarViewSet(
mixins.ActionModelMixin,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet,
):
permission_classes = [
IsAuthenticated,
ViewAndDetailPermission,
DetailPermission,
ViewPermission,
AlternatePermission1 | AlternatePermission2,
]
create_collect_serializer_class = CarWriteSerializer
create_result_serializer_class = CarDetailsSerializer
retrieve_serializer_class = CarDetailsSerializer
list_serializer_class = CarListSerializer
queryset = {}
@audoma_action(
detail=True,
methods=["get", "post"]
collectors=CarCreateRateSerializer,
results=CarRateSerializer,
errors=[CustomCarRateException]
)
def rate(self, request, pk=None, *args, **kwargs):
if request.method == "POST":
collect_serializer = kwargs.pop("collect_serializer")
instance = collect_serializer.save()
else:
instance = CarRate.objects.get_random_car_rate(car_pk=pk)
if not instance:
raise CustomCarRateException
return instance, 200
from audoma.drf import mixins
from audoma.drf import viewsets
from rest_framework.exceptions import APIException
from app.serializers import (
CarListSerializer,
CarWriteSerializer,
CarDetailsSerializer,
CarCreateRateSerializer,
CarRateSerializer
)
from app.models import (
Car,
CarRate
)
class CustomCarException(APIException):
default_detail = "Car can't be found"
status_code = 500
class CarViewSet(
mixins.ActionModelMixin,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet,
):
permission_classes = [
IsAuthenticated,
ViewAndDetailPermission,
DetailPermission,
ViewPermission,
AlternatePermission1 | AlternatePermission2,
]
create_collect_serializer_class = CarWriteSerializer
create_result_serializer_class = CarDetailsSerializer
retrieve_serializer_class = CarDetailsSerializer
list_serializer_class = CarListSerializer
queryset = {}
@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 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
from audoma.drf import mixins
from audoma.drf import viewsets
from rest_framework.exceptions import APIException
from app.serializers import (
CarListSerializer,
CarWriteSerializer,
CarDetailsSerializer,
CarCreateRateSerializer,
CarRateSerializer
)
from app.models import (
Car,
CarRate
)
class CustomCarException(APIException):
default_detail = "Car can't be found"
status_code = 500
class CarViewSet(
mixins.ActionModelMixin,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet,
):
permission_classes = [
IsAuthenticated,
ViewAndDetailPermission,
DetailPermission,
ViewPermission,
AlternatePermission1 | AlternatePermission2,
]
create_collect_serializer_class = CarWriteSerializer
create_result_serializer_class = CarDetailsSerializer
retrieve_serializer_class = CarDetailsSerializer
list_serializer_class = CarListSerializer
queryset = {}
@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 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
@audoma_action(
detail=False,
methods=["get"],
results="Car is available"
)
def active(self, request, pk=None):
instance = self.get_object(pk=pk)
if instance.active:
return None, 200
return "Car is unavailable", 200
{
"message": "Car is available"
}
from audoma.drf import mixins
from audoma.drf import viewsets
from rest_framework.exceptions import APIException
from django.conf import settings
from app.serializers import (
CarListSerializer,
CarWriteSerializer,
CarDetailsSerializer,
CarCreateRateSerializer,
CarRateSerializer
)
from app.models import (
Car,
CarRate
)
class CustomCarException(APIException):
default_detail = "Car can't be found"
status_code = 500
class CarViewSet(
mixins.ActionModelMixin,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet,
):
permission_classes = [
IsAuthenticated,
ViewAndDetailPermission,
DetailPermission,
ViewPermission,
AlternatePermission1 | AlternatePermission2,
]
create_collect_serializer_class = CarWriteSerializer
create_result_serializer_class = CarDetailsSerializer
retrieve_serializer_class = CarDetailsSerializer
list_serializer_class = CarListSerializer
queryset = {}
@audoma_action(
detail=False,
methods=["get", "post"]
collectors=CarCreateRateSerializer,
results={
"post":{201: CarRateSerializer},
"get":{200: CarDetailsSerializer, 204:"Rate service currently unavailable"}
},
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
@audoma_action(
detail=False,
methods=["get"],
results="Car is available"
)
def active(self, request, pk=None):
instance = self.get_object(pk=pk)
if instance.active:
return None, 200
return "Car is unavailable", 200
Serializer class which must inherit from serializers.BaseSerializer
@audoma_action( detail=False, methods=["post"], results=ExampleOneFieldSerializer, collectors=ExampleOneFieldSerializer, )A dictionary with HTTP methods as keys and serializer classes as values. This allows defining different collector for each HTTP method.
@audoma_action( detail=True, methods=["post"], collectors={"post": ExampleModelCreateSerializer}, results=ExampleModelSerializer, )
Note
Note
Serializer class or which must inherit from serializers.BaseSerializer or string variable In this case, the serializer class passed will be used to produce every response coming from this action.
@audoma_action( detail=True, methods=["put", "patch"], collectors=ExampleModelCreateSerializer, results=ExampleModelSerializer, )A dictionary with HTTP methods as keys and serializer classes or string variables as values. In This case, there will be a different response serializer for each HTTP method.
@audoma_action( detail=False, methods=["get", "post"], collectors={"post": MyCreateSerializer}, results={"post": MySerializer, "get": MyListSerializer} )A dictionary with HTTP methods as keys and dictionaries as values. Those dictionaries have status codes as keys and serializer classes or string variables as values.
@audoma_action( detail=False, methods=["post"], collectors={"post": MyCreateSerializer}, results={"post": {201: MySerializer, 204: MyNoContentSerializer}} )
Note
- NotFound
- NotAuthenticated
- AuthenticationFailed
- ParseError
- PermissionDenied
COMMON_API_ERRORS = [
myexceptions.SomeException
]
Note
from audom.drf import serializers
class SalesContactSerializer(serializers.Serializer):
phone_number = serializers.PhoneNumberField(example="+48 123 456 789")
name = serializers.CharField(example="John", max_length=255)
from rest_framework import fields
from audoma.mixins import ExampleMixin
from audoma.examples import NumericExample,
class SaleAmountField(ExampleMixin, fields.Field):
audoma_example_class = NumericExample
- NumericExample
- RegexExample
- Example
from audoma.examples import Example
class SaleExample(Example):
def generate_value(self):
return f"{self.amount} $"
from audoma.django.db import models
class SalesmanStats(models.Model):
salesman = models.ForeignKey("sale.Salesman"e, on_delete=models.CASCADE)
earned = models.MoneyField(max_digits=14, decimal_places=2, default_currency="PLN")
stats = SalesmanStats.objects.get(id=20)
# Simply pass the Money object
stats.earned = Money("99900.23", "PLN")
# You may also pass those variables to objects.create separately
sales = Salesman.objects.get(id=1)
stats = SalesmanStats.objects.create(
salesman=sales, earned_amount=120,
earned_courrency="PLN"
)
# In our case we defined the default currency, so it also may be
stats = SalesmanStats.objects.create(
salesman=sales, earned_amount=120
)
# To get the amount we type
print(stats.earned) # this will print 120
print(stats.earned.currency) # will print PLN
from audoma.django.db import models
class SalesmanStats(models.Model):
salesman = models.ForeignKey("sale.Salesman", on_delete=models.CASCADE)
earned = models.MoneyField(max_digits=14, decimal_places=2, default_currency="PLN")
phone_number = models.PhoneNumberField(region="GB")
{
"salesman": 1,
"earned": 500,
"phone_number": "+44 20 7894 5678",
}
- viewname - the name of a view from which variables should be retrieved
- value_field - field name from which value should be retrieved
- display_field - field name from which display value should be retrieved
{
"x-choices": {
"choices": {
"value1": "displayValue1",
"value2": "displayValue2",
"value3": "displayValue3",
"value4": "displayValue4",
}
}
}
{
"x-choices": {
"operationRef": "#/paths/manufacturer_viewset~1",
"value": "$response.body#results/*/id",
"display": "$response.body#results/*/name"
}
}