Django ManyToMany сквозное поле с JSON:API и Swagger
Я создал сериализатор, который имеет поле под названием products_list, которое является Nested.
Моя проблема в том, что я использую автогенерируемую документацию Swagger с drf-yasg, и это поле не появляется в ожидаемом теле для конечной точки.
Я установил и скопировал SWAGGER_SETTINGS на https://github.com/glowka/drf-yasg-json-api
Мой код схемы документации выглядит следующим образом:
from drf_yasg import openapi
from drf_yasg.generators import OpenAPISchemaGenerator
from drf_yasg.views import get_schema_view
class BothHttpAndHttpsSchemaGenerator(OpenAPISchemaGenerator):
def get_schema(self, request=None, public=False):
schema = super().get_schema(request, public)
schema.schemes = ["http", "https"]
return schema
schema_view = get_schema_view(
openapi.Info(
title="Mobile quoter API",
default_version='1.0.0',
description="API managing quoting app",
terms_of_service="https://www.google.com/policies/terms/",
),
generator_class=BothHttpAndHttpsSchemaGenerator,
public=True,
)
Вот мои сериализаторы:
class QuoteSerializer(serializers.ModelSerializer):
"""Serializer for Quotes"""
products_list = QuoteProductsSerializer(many=True, write_only=True,
required=True)
extra_info = serializers.CharField(required=False)
class Meta:
model = Quote
fields = (
'client',
'extra_info',
'products_list',
'generated_by',
'closed_at',
'created_at',
'cost',
)
extra_kwargs = {
'quoteproducts_set': {'write_only': True},
'generated_by': {'read_only': True},
'closed_at': {'read_only': True},
'created_at': {'read_only': True},
'cost': {'read_only': True},
}
Мои модели:
class Quote(SafeModel):
client = models.ForeignKey(Client, on_delete=models.PROTECT)
generated_by = models.ForeignKey(User, on_delete=models.PROTECT)
closed_at = models.DateTimeField(blank=True, null=True)
extra_info = models.TextField(blank=True)
cost = models.DecimalField(max_digits=9, decimal_places=2)
products = models.ManyToManyField(Product, through='QuoteProducts')
# Quote model does not have a created_at field because SafeModel has one
class QuoteProducts(Model):
product = models.ForeignKey(Product, on_delete=models.PROTECT)
quote = models.ForeignKey(Quote, on_delete=models.PROTECT)
quantity = models.IntegerField()
price = models.DecimalField(max_digits=7, decimal_places=2)
class Meta:
constraints = [
models.UniqueConstraint(
fields=('quote', 'product'),
name='quote_product_unique_constraint',
),
]
И это мое мнение для цитат:
from rest_framework import status
from rest_framework.response import Response
from mobilequoter.helper.views import CRViewSet
from mobilequoter.quote.handlers import QuoteAPIHandler
from mobilequoter.quote.models import Quote
from mobilequoter.quote.serializers.quote import QuoteSerializer
class QuoteViewSet(CRViewSet):
"""View to Create and Read Quotes."""
queryset = Quote.objects.all()
serializer_class = QuoteSerializer
def create(self, request, *args, **kwargs):
data = QuoteAPIHandler.handle(request)
return Response(data, status=status.HTTP_201_CREATED)
Хотя я переопределяю метод create, я не думаю, что это причина моей проблемы.
Как я уже сказал, моя проблема заключается в том, что в документации swagger autogenerated API, вот что я получаю для тела конечной точки Quote POST:
Почти все хорошо, кроме того, что в теле не сказано, что оно ожидает поле products_list. Я хотел бы знать, в чем именно моя ошибка. Я пытался читать документацию (https://django-rest-framework-json-api.readthedocs.io/en/stable/usage.html) , но насколько я понял, там не показан случай использования с сквозным полем M2M.
В качестве примечания, я считаю, что все мои проблемы были бы решены, если бы существовал способ использовать JSON API при выводе данных, чтобы они имели формат JSON API, но на входе (POST, PUT или PATCH), только получать данные следующим образом:
Возможно ли это? И является ли это вообще хорошей практикой?
Спасибо за ваше время, любой ответ будет оценен по достоинству.
Я заставил его работать. В моем QuoteSerializer я заменил:
products_list = QuoteProductsSerializer(many=True, write_only=True,
required=True)
с:
products_list = serializers.ListField(
child=QuoteProductsSerializer(),
write_only=True,
required=True
)
В документации показано, как и ожидалось:
Я бы хотел, чтобы не нужно было указывать все эти "type" для POST-запросов, а чтобы он сам выводил их, но для меня это приемлемо. По крайней мере, документация API сама показывает значение типа в запросе.