unable to specify the fields in drf queryset using drf-yasg - django

class ABC(generics.ListCreateApiView):
#swagger_auto_schema(
operation_description="THIS API IS TO CREATE MESSAGES IN A LIST ",
auto_schema=AcceptFormDataSchema,
request_body=MessageGetSerializer
)
def get_queryset(self):
data =self.request.GET.get("code")
...
#swagger_auto_schema(
operation_description="THIS API IS TO CREATE MESSAGES IN A LIST ",
request_body=openapi.Schema(
type=openapi.TYPE_OBJECT,
required=["data"],
properties={
"code": openapi.Schema(type=openapi.TYPE_STRING),
},
)
)
def post(self, request):
brand_code = request.data.get("code")
...
#serializer.py
class MessageSerializer(serializers.ModelSerializer):
class Meta:
model = Messages
fields = ("message_id", "content", "description")
My post method is working fine with the fields which I required using the same serializer but it's not working for the get_queryset method. Can anyone please suggest something on how I will get the fields using drf-yasg?

You shouldn't decorate get_queryset as that is an internal function and not part of the ApiView's endpoints. You probably see a get request in swagger because the ListCreateApiView you're using defines handlers for get and post methods.
Since you're not overriding the get method handler, you can inject a decorator into the ApiView's get method using Django's method_decorator, as indicated in drf-yasg section on swagger_auto_schema decorator.
The following is an example implementation to your ApiView that should also document the get endpoint.
#method_decorator(
name='get',
decorator=swagger_auto_schema(
operation_description="description from swagger_auto_schema via method_decorator"
)
)
class ABC(generics.ListCreateApiView):
serializer_class = MessageSerializer
def get_queryset(self):
data =self.request.GET.get("code")
...
#swagger_auto_schema(
operation_description="THIS API IS TO CREATE MESSAGES IN A LIST ",
request_body=openapi.Schema(
type=openapi.TYPE_OBJECT,
required=["data"],
properties={
"code": openapi.Schema(type=openapi.TYPE_STRING),
},
)
)
def post(self, request):
brand_code = request.data.get("code")
...
--------------
#serializer.py
class MessageSerializer(serializers.ModelSerializer):
class Meta:
model = Messages
fields = ("message_id", "content", "description")

Related

Django: multiple update

I want to update multiple instances of my models. I can currently update them one by one just fine.
I want to be able to update them with a PUT request to my URL:
www.my-api.com/v1/mymodels/
with the request data like so:
[ { "mymodel_id": "id1", "name": "foo"}, { "mymodel_id": "id2", "alert_name": "bar"} ]
If I try it this way now, I receive the following Django error:
Serializers with many=True do not support multiple update by default, only multiple create.
For updates it is unclear how to deal with insertions and deletions.
If you need to support multiple update, use a `ListSerializer` class and override `.update()` so you can specify the behavior exactly.
My model has a Serializer class MyModelSerializer
class MyModelSerializer(ModelSerializerWithFields):
class Meta:
model = MyModel
fields = "__all__"
def to_representation(self, instance):
data = super().to_representation(instance)
if instance.name is None:
del data['name']
return data
ModelSerializerWithFields extends serializers.ModelSerializer.
The View for MyModel is very basic:
class MyModelViewSet(MultipleDBModelViewSet):
serializer_class = MyModelSerializer
queryset = MyModel.objects.none()
MultipleDBModelViewSet extends BulkModelViewSet, and contains
def filter_queryset(self, queryset):
ids = self.request.query_params.get("ids", None)
if ids:
return queryset.filter(pk__in=json.loads(ids))
# returns normal query set if no param
return queryset
At which level do I need to use the ListSerializer class? ie: in ModelSerializerWithFields or in MyModelSerializer? Or somewhere else completely?
If anyone has any examples of this implementation, I'd be very grateful
Serializer Must be inherited from BulkSerializerMixin
So the serializer code will be like
from rest_framework_bulk.serializers import BulkListSerializer, BulkSerializerMixin
class SimpleSerializer(BulkSerializerMixin,
ModelSerializer):
class Meta(object):
model = SimpleModel
# only required in DRF3
list_serializer_class = BulkListSerializer
At Viewset don't forget to use the
filter_queryset method.
So your view will be like
class MyModelViewSet(MultipleDBModelViewSet):
serializer_class = MyModelSerializer
queryset = MyModel.objects.none()
def filter_queryset(self, queryset):
return queryset.filter(<some_filtering>)

DRF YASG Customizing

I'm trying to customize my api documentation buuild with yasg.
First off, I would like to determine the naming of my own sections, and what endpoints should be included in this section. It seems that the naming of sections is based on the first prefix not belonging to the longest common prefix e.g.:
if we have the urls api/v1/message and api/v1/test than the sections will be named message and test. Is there a way for me to determine A custom naming for this section?
Furthermore, the introduction of every section is empty, how do I add text here?
And last but not least, Stripe has these amazing section dividers how can I add these in drf yasg.
Currently, I'm using APIView and #swagger_auto_schema to define the documentation of my endpoint.
In the code below, you can see how to add more information to define your endpoint. I hope it helps you
##serializers.py
class CategorySerializer(serializers.ModelSerializer):
"""
Serializing Categories
"""
class Meta:
model = Category
fields = [
'id', 'name', 'slug'
]
read_only_fields = [
'slug',
]
##views.py
username_param = openapi.Parameter('username', in_=openapi.IN_QUERY, description='Username',
type=openapi.TYPE_STRING)
email = openapi.Parameter('email', in_=openapi.IN_QUERY, description='Email',
type=openapi.TYPE_STRING)
category_response = openapi.Response('response description', CategorySerializer)
class CategoryList(APIView):
permission_classes = [AllowAny]
#swagger_auto_schema(
manual_parameters=[username_param, email],
query_serializer=CategorySerializer,
responses = {
'200' : category_response,
'400': 'Bad Request'
},
security=[],
operation_id='List of categories',
operation_description='This endpoint does some magic',
)
def get(self, request, format=None):
"""
GET:
Return a list of all the existing categories.
"""
categories = Category.objects.all()
serializer = CategorySerializer(categories, many=True)
return Response(serializer.data)
#swagger_auto_schema(
request_body=CategorySerializer,
query_serializer=CategorySerializer,
responses={
'200': 'Ok Request',
'400': "Bad Request"
},
security=[],
operation_id='Create category',
operation_description='Create of categories',
)
def post(self, request, format=None):
"""
POST:
Create a new category instance.
"""
serializer = CategorySerializer(data=request.data)
if serializer.is_valid():
serializer.save(created_by=self.request.user)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
By last, if you want to see your endpoints in groups by link, you can test commenting the line below in yours urls.py
#urlpatterns = format_suffix_patterns(urlpatterns)
Below, some screen of how you should see it
You can find more information in the following link: https://drf-yasg.readthedocs.io/en/stable/custom_spec.html

Method Not Allowed - Django Rest Framework

I am getting error while updating. When i add data it is added successfully. This error is only comming for UpdateAPIView
{
"detail": "Method \"POST\" not allowed."
}
urls.py
path('groups/update/<int:pk>', views.GroupsUpdateAPIView.as_view(), name='api_groups_update'),
Views.py
class GroupsUpdateAPIView(generics.UpdateAPIView):
queryset = Groups.objects.all()
serializer_class = GroupsAddSerialzer
permission_classes = [UserIsAuthenticated]
def perform_update(self, serializer):
serializer.save(
group_updated_by = self.request.auth.application.user,
)
Serializer.py
class GroupsAddSerialzer(serializers.ModelSerializer):
class Meta:
model = Groups
fields = ['group_name', 'group_description', 'group_status']
The UpdateAPIView view uses the HTTP methods PUT and PATCH. The method POST is used to create a new instance with CreateAPIView view.

POST, PUT, DELETE in Django RESTful framework not working

I'd like to post to my Django server using POST so I can add a employee item.
views.py
class EmployeeList(generics.ListAPIView):
queryset = Employee.objects.all()
serializer_class = EmployeeSerializer
class EmployeeDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Employee.objects.all()
serializer_class = EmployeeSerializer
models.py
class Employee(models.Model):
firstname=models.CharField(max_length=10)
lastname=models.CharField(max_length=10)
emp_id=models.IntegerField()
def __str__(self):
return self.firstname;
serializer.py
class EmployeeSerializer(serializers.ModelSerializer):
class Meta:
model=Employee
# fields={'firstname','lastname'}
fields='__all__'
My POST request:
{
"id": 8,
"firstname": "zxcvb",
"lastname": "bnmmm",
"emp_id": 3
}
Error I got:
{
"detail": "Method \"POST\" not allowed."
}
Even after using generics.RetrieveUpdateDestroyAPIView I can't POST, PUT or DELETE.
PS: I’m new to Django REST framework.
To provide a post method handler you'll need a view with a create() action (CreateAPIView or ListCreateAPIView). Check the documention about concrete view classes.
Change your EmployeeList view to inherit from ListCreateAPIView for add read-write endpoints to represent a collection of employees
class EmployeeList(generics.ListCreateAPIView):
queryset = Employee.objects.all()
serializer_class = EmployeeSerializer
Now, just register the views with the URL conf as usual
urlpatterns = [
path('employees/', EmployeeList, name='employee-list'),
path('employees/<int:pk>/', EmployeeDetail, name='employee-detail'),
]
Concrete view classes map methods defined in mixins such ListModelMixin, CreateModelMixin, RetrieveModelMixin, etc. to HTTP methods. By defaults, theese actions (see here) are mapped to the following HTTP methods
list - get
create - post
retrieve - get
update - put
partial_update - patch
destroy - delete

Unable to get a non-model field in the validated_data of a Django Rest Framework serializer

I have an ItemCollection and Items in my Django models and I want to be able to remove Items from the collection through the UI. In a REST PUT request I add an extra boolean field deleted for each Item to signal that an Item should be deleted.
The correct way to handle this seems to be in the update method of the Serializer.
My problem is that this non-model deleted field gets removed during validation, so it is not available anymore. Adding deleted as a SerializerMethodField did not help. For now I get my deleted information from the initial_data attribute of the Serializer, but that does not feel right.
My current example code is below. Does anybody know a better approach?
Models:
class ItemCollection(models.Model):
description = models.CharField(max_length=256)
class Item(models.Model):
collection = models.ForeignKey(ItemCollection, related_name="items")
Serializers:
from django.shortcuts import get_object_or_404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from models import Item, ItemCollection
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
class ItemCollectionSerializer(serializers.ModelSerializer):
items = ItemSerializer(many=True, read_only=False)
class Meta:
model = ItemCollection
def update(self, instance, validated_data):
instance.description = validated_data['description']
for item, item_obj in zip(
self.initial_data['items'], validated_data['items']):
if item['delete']:
instance.items.filter(id=item['id']).delete()
return instance
class ItemCollectionView(APIView):
def get(self, request, ic_id):
item_collection = get_object_or_404(ItemCollection, pk=ic_id)
serialized = ItemCollectionSerializer(item_collection).data
return Response(serialized)
def put(self, request, ic_id):
item_collection = get_object_or_404(ItemCollection, pk=ic_id)
serializer = ItemCollectionSerializer(
item_collection, data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data)
And an example of the json in the PUT request:
{
"id": 2,
"items": [
{
"id": 3,
"collection": 2,
"delete": true
}
],
"description": "mycoll"
}
You can add non-model fields back by overwriting the to_internal_value fn:
def to_internal_value(self, data):
internal_value = super(MySerializer, self).to_internal_value(data)
my_non_model_field_raw_value = data.get("my_non_model_field")
my_non_model_field_value = ConvertRawValueInSomeCleverWay(my_non_model_field_raw_value)
internal_value.update({
"my_non_model_field": my_non_model_field_value
})
return internal_value
Then you can process it however you want in create or update.
If you're doing a PUT request, your view is probably calling self.perform_update(serializer). Change it for
serializer.save(<my_non_model_field>=request.data.get('<my_non_model_field>', <default_value>)
All kwargs are passed down to validated_data to your serializer.
Make sure to properly transform incoming value (to boolean, to int, etc.)