Abdul Rehman
Abdul Rehman

Reputation: 5654

Django Rest Framework validation in POST method of APIView

I'm new to DRF and trying to build a rest api, I need to make an api for task executions not just for CRUD, that's why I have override the POST method of APIView as:

class DeploymentsList(viewsets.ModelViewSet):
    queryset = DeploymentOnUserModel.objects.all()
    serializer_class = DeploymentonUserSerializer

    def create(self, request, *args, **kwargs):
        """overwrite this for extra actions"""
        serializer = self.serializer_class(data=request.data)
        if serializer.is_valid(raise_exception=True):
            print('valid request')
            serializer.save()
            return Response(serializer.data)
        else:
            print('Something invalid')
            return Response('Invalid request')

models.py:

    services = (
    ('Single', 'Single'),
    ('Multiple', 'Multiple'),
)


class DeploymentOnUserModel(models.Model):
    deployment_name = models.CharField(max_length=256, )
    credentials = models.TextField(blank=False)
    project_name = models.CharField(max_length=150, blank=False)
    project_id = models.CharField(max_length=150, blank=True)
    cluster_name = models.CharField(max_length=256, blank=False)
    zone_region = models.CharField(max_length=150, blank=False)
    services = models.CharField(max_length=150, choices=services)
    configuration = models.TextField()
    routing = models.TextField()

    def save(self, **kwargs):
        if not self.id and self.services == 'Multiple' and not self.routing and not self.configuration:
            raise ValidationError("You must have to provide routing for multiple services deployment.")
        super().save(**kwargs)

serializers.py:

class DeploymentonUserSerializer(serializers.ModelSerializer):
    model = DeploymentOnUserModel
    fields = '__all__'
    readonly_fields = 'pk'

urls.py:

app_name = 'deployments'

urlpatterns = [
    path('deployments/', apiview.DeploymentsList.as_view({'get': 'list', 'post': 'create'}), name='deployment_list'),
    path('deployments/<int:pk>', apiview.DeploymentDetail.as_view(), name='deployment_detail')

]

Error returns:

AttributeError: 'str' object has no attribute 'values'

Update: Full Traceback

Internal Server Error: /api/v1/deployments/
Traceback (most recent call last):
  File "/Users/abdul/KontainerApi/lib/python3.6/site-packages/django/core/handlers/exception.py", line 35, in inner
    response = get_response(request)
  File "/Users/abdul/KontainerApi/lib/python3.6/site-packages/django/core/handlers/base.py", line 128, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/Users/abdul/KontainerApi/lib/python3.6/site-packages/django/core/handlers/base.py", line 126, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/abdul/KontainerApi/lib/python3.6/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "/Users/abdul/KontainerApi/lib/python3.6/site-packages/rest_framework/viewsets.py", line 95, in view
    return self.dispatch(request, *args, **kwargs)
  File "/Users/abdul/KontainerApi/lib/python3.6/site-packages/rest_framework/views.py", line 494, in dispatch
    response = self.handle_exception(exc)
  File "/Users/abdul/KontainerApi/lib/python3.6/site-packages/rest_framework/views.py", line 454, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/Users/abdul/KontainerApi/lib/python3.6/site-packages/rest_framework/views.py", line 491, in dispatch
    response = handler(request, *args, **kwargs)
  File "/Users/abdul/PycharmProjects/KontainerApi/deployments/apiview.py", line 15, in create
    serializer.is_valid(raise_exception=False)
  File "/Users/abdul/KontainerApi/lib/python3.6/site-packages/rest_framework/serializers.py", line 236, in is_valid
    self._validated_data = self.run_validation(self.initial_data)
  File "/Users/abdul/KontainerApi/lib/python3.6/site-packages/rest_framework/serializers.py", line 435, in run_validation
    value = self.to_internal_value(data)
  File "/Users/abdul/KontainerApi/lib/python3.6/site-packages/rest_framework/serializers.py", line 459, in to_internal_value
    fields = self._writable_fields
  File "/Users/abdul/KontainerApi/lib/python3.6/site-packages/django/utils/functional.py", line 36, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "/Users/abdul/KontainerApi/lib/python3.6/site-packages/rest_framework/serializers.py", line 370, in _writable_fields
    field for field in self.fields.values()
AttributeError: 'str' object has no attribute 'values'
[27/Jun/2018 16:11:41] "POST /api/v1/deployments/ HTTP/1.1" 500 15073

But I don't know how can I validate the request? when I'm overriding the APIView's POST method.

Post data:

  {
"deployment_name": "dep5",
"credentials": "cre4",
"project_name": "pro4",
"project_id": "004",
"cluster_name": "clus4",
"zone_region": "zon4",
"services": "Single",
"configuration": "conf4",
"routing": "route4"

}

Upvotes: 0

Views: 7587

Answers (2)

Enthusiast Martin
Enthusiast Martin

Reputation: 3091

It is not very clear what you want to achieve.

I would say that you want to perform other task after you receive a request, and after the input data is validated.

Although not sure what kind of task you want to perform and when - before or after your model is saved.

but you can do it part of create like this prior to creating your model:

 def create(self, request, *args, **kwargs):
    serializer = self.get_serializer(data=request.data)
    serializer.is_valid(raise_exception=True)

     **.... your task - access to data via serializer.validated_data....**

    self.perform_create(serializer)        
    return Response(serializer.data)

or if you want to perform something after your model is created, you can override perform_create ( as we see from implementation of create method, it is called just after input is validated)

def perform_create(self, serializer):
    instance =  serializer.save()
     .... your task ....

Updated to address the problem "AttributeError: 'str' object has no attribute 'values'"

The problem is with your use of viewset - Viewset gives you a "resource control view" which you can use to list,retrive, update, delete a resource.

You don't need to explicitly registers views in urlconf for list/create and then detail.

Instead, do it with a router class and it will give you all the views ( including the detail):

router = DefaultRouter()
router.register(r'deployment', DeploymentsList, base_name='deployment')
urlpatterns = router.urls

and now you can do:

  • GET deployment/
  • POST deployment
  • GET deployment/{id} -> detail

Another issue that you have is with the validation. I dont recommend raising ValidationError in save function of a model.

There is a serializer to handle your validation and you can do this in validate function in your DeploymentUserSerializer

def validate(self, attrs):
    # perform your validation here
    return attrs

Upvotes: 1

anjaneyulubatta505
anjaneyulubatta505

Reputation: 11665

You can use ListCreateAPIView like below. We can directly use ListCreateAPIView in urls.py also.

serializers.py

from rest_framework import serializers
from .models import DeploymentOnUserModel

class DeploymentOnUserModelSerializer(serializers.ModelSerializer)
    class Meta:
        model = DeploymentOnUserModel
        fields = [
            'deployment_name', 'credentials', 'project_name', 
            'project_id', 'cluster_name', 'zone_region', 'services',
            'configuration', 'routing']

views.py

from rest_framework.generics import ListCreateAPIView
from .models import DeploymentOnUserModel
from .serializers import DeploymentOnUserModelSerializer 

class DeploymentsList(ListCreateAPIView):
     queryset = DeploymentOnUserModel.objects.all()
     serializer_class = DeploymentOnUserModelSerializer

Your answer Error Fix

def create(self, request, *args, **kwargs):
    """overwrite this for extra actions"""
    serializer = self.serializer_class(data=request.data)
    serializer.is_valid(raise_exception=True)
    serializer.save()
    return Response(serializer.data)

Reference: https://github.com/encode/django-rest-framework/blob/master/rest_framework/generics.py

Upvotes: 0

Related Questions