0

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"

}

3
  • what you need to validate? serializers are for validation Commented Jun 27, 2018 at 12:32
  • Hi @SaeedBabashahi, I need to validate the incoming data, how can I use serializers here? Commented Jun 27, 2018 at 12:33
  • 1
    read docs at django-rest-framework.org/api-guide/serializers Commented Jun 27, 2018 at 12:35

2 Answers 2

1

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
Sign up to request clarification or add additional context in comments.

2 Comments

How can I use urls because now it returns: raise TypeError("The actions argument must be provided when " TypeError: The actions argument must be provided when calling .as_view() on a ViewSet. For example .as_view({'get': 'list'})
this is something else. For this look at routers in the DRF documentation ( django-rest-framework.org/api-guide/routers )
0

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

10 Comments

Hi @AnjaneyuluBatta, I need to override the post request.
@AbdulRehman serializer will automatically validate the POST data. why you want to override POST method ?
Because I need to get the request data and parse it for further executions, stackoverflow.com/q/51061850/7644562
It will automatically returns the serializer.data which is taken from request.data.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.