1

I am new to django and drf. I've created a viewset for getting list of elements, adding new, updating and deleting, but I have some troubles when I try to create a viewset for getting detailed info about 1 element.

Here is my urls.py

urlpatterns = [
    path('tasks', views.TaskViewset.as_view(
        {
            'get':'list',
            'post':'add',
            'put':'update',
            'delete':'delete'
        }
    )),
    path('tasks/<int:pk>', views.TaskDetailViewset.as_view(
        {
            'get':'detail'
        }
    ))
]

and here is my TaskDetailViewset from views.py

class TaskDetailViewset(viewsets.ViewSet):

    def detail(self, request, pk, *args, **kwargs):
        #data = request.query_params
        task = models.Task.objects.get(id=pk)
        serializer = serializers.TaskSerializer(task)

        if serializer.is_valid():
            return Response(data=serializer.data, status=status.HTTP_200_OK)
        return Response(status=status.HTTP_404_NOT_FOUND)

When I try to send request to http://127.0.0.1:8000/api/v1/tasks/1 I get 'NoneType' object is not callable and I don't understand where is the problem here

Traceback:

Environment:


Request Method: GET
Request URL: http://127.0.0.1:8000/api/v1/tasks/1

Django Version: 4.1.2
Python Version: 3.10.0
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'rest_framework',
 'todo']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']



Traceback (most recent call last):
  File "/Users/aleksandr/Documents/projects/todoist/venv/lib/python3.10/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
  File "/Users/aleksandr/Documents/projects/todoist/venv/lib/python3.10/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/aleksandr/Documents/projects/todoist/venv/lib/python3.10/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "/Users/aleksandr/Documents/projects/todoist/venv/lib/python3.10/site-packages/rest_framework/viewsets.py", line 125, in view
    return self.dispatch(request, *args, **kwargs)
  File "/Users/aleksandr/Documents/projects/todoist/venv/lib/python3.10/site-packages/rest_framework/views.py", line 509, in dispatch
    response = self.handle_exception(exc)
  File "/Users/aleksandr/Documents/projects/todoist/venv/lib/python3.10/site-packages/rest_framework/views.py", line 469, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/Users/aleksandr/Documents/projects/todoist/venv/lib/python3.10/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
    raise exc
  File "/Users/aleksandr/Documents/projects/todoist/venv/lib/python3.10/site-packages/rest_framework/views.py", line 506, in dispatch
    response = handler(request, *args, **kwargs)

Exception Type: TypeError at /api/v1/tasks/1
Exception Value: 'NoneType' object is not callable

Update: I found in interactive debug this Here I see that handler is None but I still dont't get why

Update 2:

from rest_framework import serializers

from todo.models import Task

class TaskSerializer(serializers.Serializer):
    id = serializers.IntegerField(
        read_only=True
    )

    title = serializers.CharField(
        required = True
    )

    content = serializers.CharField(
        required=False
    )

    done = serializers.BooleanField(
        required=True
    )

    def create(self, validated_data):
        return Task.objects.create(**validated_data)

    def update(self, instance, validated_data):
        instance.title = validated_data.get('title', instance.title)
        instance.content = validated_data.get('content', instance.content)
        instance.done = validated_data.get('done', instance.done)
        instance.save()
        return instance
0

3 Answers 3

1

By rebuilding a very small experimental project, I was able to solve this problem by understanding a series of problems below and I have put the form that you can properly do what you want here for you:

  1. It seems that this error you are receiving (Exception Value: 'NoneType' object is not callable) is related to the name of the detail (honestly, I have not understood the reason yet and I'm searching for it) and by changing it to any other name (really any other name 😄), this error will be resolved, according to your codes like this will be:

urls.py

    path('tasks/<int:pk>', views.TaskDetailViewset.as_view(        {
            'get':'any_other_thing'
        }))
]

views.py

class TaskDetailViewset(viewsets.ViewSet):

    def any_other_thing(self, request, pk): # you can send more params like *args or **kwrags and they are Ok
        ...
  1. After changing point 1 in your codes, with sending a request to mentioned url you'll encounter the following error:
AssertionError: Cannot call `.is_valid()` as no `data=` keyword argument was passed when instantiating the serializer instance.

which will be solved by adding a data= argument name before task on the line where you build serializer:

    def any_other_thing(self, request, pk):
        ...
        serializer = TaskSerializer(data=task)
        ...
  1. After making the above change, you will still have problems in creating the serializer, and if you have made the serializer errors by placing the following line:
return Response(status=status.HTTP_404_NOT_FOUND)

In the code related to the view instead of

return Response(data=serializer.errors, status=status.HTTP_404_NOT_FOUND)

You can understand that now the error related to the non-field-error (Instead of passing a dictionary, you passed a "task" object):

{
    "non_field_errors": [
        "Invalid data. Expected a dictionary, but got Task."
    ]
}

By adding the following function with the task argument instead of passing the task directly to TaskSerializer, this problem will also be solved and you will finally be able to receive an answer successfully.

class TaskDetailViewset(viewsets.ViewSet):

    def any_other_thing(self, request, pk):
        ...
        serializer = TaskSerializer(data=model_to_dict(task))
        ...

Therefore, your code snippets will be as follows:

urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('tasks', views.TaskViewset.as_view(
        {
            'get':'list',
            'post':'add',
            'put':'update',
            'delete':'delete'
        }
    )),
    path('tasks/<int:pk>', views.TaskDetailViewset.as_view(        {
            'get':'any_other_thing'
        }))
]

views.py

from django.forms.models import model_to_dict
from rest_framework.response import Response
from rest_framework import status, viewsets

from todo.models import Task
from todo.serilizers import TaskSerializer


class TaskDetailViewset(viewsets.ViewSet):

    def any_other_thing(self, request, pk, *args, **kwargs):
        #data = request.query_params
        task = Task.objects.get(id=pk)
        serializer = TaskSerializer(data=model_to_dict(task))

        if serializer.is_valid():
            return Response(data=serializer.data, status=status.HTTP_200_OK)
        return Response(data=serializer.errors, status=status.HTTP_404_NOT_FOUND)
Sign up to request clarification or add additional context in comments.

1 Comment

1

I found a reason of my problem but still don't get how it works. The problem was in th name of my method detail. When I changed name to details everything started to work fine.

Now my urls.py looks like this

urlpatterns = [
    path('tasks', views.TaskViewset.as_view(
        {
            'get':'list',
            'post':'add',
            'put':'update',
            'delete':'delete'
        }
    )),
    path('tasks/<int:pk>', views.TaskDetailViewset.as_view(
        {
            'get': 'details'
            }
    ))
]

My TaskDetailViewset looks like this:

class TaskDetailViewset(viewsets.ViewSet):

    def details(self, request, pk):
        task = models.Task.objects.get(id=pk)
        serializer = serializers.TaskSerializer(data=task.__dict__)

        if serializer.is_valid():
            return Response(data=serializer.data, status=status.HTTP_200_OK)
        return Response(serializer.errors, status=status.HTTP_404_NOT_FOUND)

Thank you all for helping me!

Comments

0

in last return add this:

return Response(serializer.errors, status=status.HTTP_404_NOT_FOUND)

2 Comments

It doesn't make any changes.
please check django-rest-framework.org/api-guide/viewsets because maybe you use ViewSet in not a good way

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.