Django rest framework TypeError: 'NoneType' object is not callable

Question:

/models.py

class Like(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL,
                             related_name='likes',
                             on_delete=models.CASCADE)
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

class Content(models.Model):
    """Контент (фильмы, сериалы, и т.д.)"""
    title_ru = models.CharField('Название', max_length=200, blank=True)
    title_en = models.CharField('Название на английском', max_length=200, blank=True)
    rating = models.FloatField('Оценка', validators=[
        MaxValueValidator(10.0),
        MinValueValidator(0.0),
    ], default=0.0)
    year = models.PositiveSmallIntegerField('Дата выхода', default=date.today().year, null=True)
    kinopoisk_id = models.IntegerField('Кинопоиск id')
    is_banned = models.BooleanField('Бан', default=False)


    def __str__(self):
        return f'{self.title_ru} / {self.title_en}'

    class Meta:
        ordering = ['-year']
        verbose_name = 'Контент'
        verbose_name_plural = 'Контент'

/services.py

from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType


User = get_user_model()


def add_like(obj, user):
    """Лайк пользователя"""
    obj_type = ContentType.objects.get_for_model(obj)
    like, is_created = Like.objects.get_or_create(
        content_type=obj_type, object_id=obj.id, user=user)
    return like


def remove_like(obj, user):
    """Лайкает `obj`."""
    obj_type = ContentType.objects.get_for_model(obj)
    Like.objects.filter(
        content_type=obj_type, object_id=obj.id, user=user
    ).delete()


def is_fan(obj, user) -> bool:
    """Удаляет лайк с `obj`."""
    if not user.is_authenticated:
        return False
    obj_type = ContentType.objects.get_for_model(obj)
    likes = Like.objects.filter(
        content_type=obj_type, object_id=obj.id, user=user)
    return likes.exists()


def get_fans(obj):
    """Получает всех пользователей, которые лайкнули `obj`."""
    obj_type = ContentType.objects.get_for_model(obj)
    return User.objects.filter(
        likes__content_type=obj_type, likes__object_id=obj.id)

/serializers.py

from django.contrib.auth import get_user_model
from rest_framework import serializers

from .services import is_fan


User = get_user_model()


class FanSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = (
            'username',
        )

class ContentListSerializer(serializers.ModelSerializer):
    """Список контента"""

    class Meta:
        model = Content
        fields = ('title_ru', 'title_en', 'kinopoisk_id', 'rating', 'year')


class ContentDetailSerializer(serializers.ModelSerializer):
    """Информация о контенте"""
    is_fan = serializers.SerializerMethodField()

    class Meta:
        model = Content
        exclude = ('is_banned', 'id', 'kinopoisk_id')

    def get_is_fan(self, obj) -> bool:
        """Проверяет, лайкнул ли `request.user` твит (`obj`)."""
        user = self.context.get('request').user
        return is_fan(obj, user)

/mixins.py

from rest_framework.decorators import action
from rest_framework.response import Response

from .serializers import FanSerializer
from .services import get_fans, remove_like, add_like


class LikedMixin:
    @action(detail=True, methods=['POST'])
    def like(self, request, pk=None):
        """Лайкает `obj`."""
        obj = self.get_object()
        add_like(obj, request.user)
        return Response()

    @action(detail=True, methods=['POST'])
    def unlike(self, request, pk=None):
        """Удаляет лайк с `obj`."""
        obj = self.get_object()
        remove_like(obj, request.user)
        return Response()

    @action(detail=True, methods=['GET'])
    def fans(self, request, pk=None):
        """Получает всех пользователей, которые лайкнули `obj`."""
        obj = self.get_object()
        fans = get_fans(obj)
        serializer = FanSerializer(fans, many=True)
        return Response(serializer.data)

/view

from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticatedOrReadOnly

from .mixins import LikedMixin
from .models import Content
from .serializers import ContentListSerializer, ContentDetailSerializer


class ContentViewSet(LikedMixin, viewsets.ModelViewSet):
    """Вывод деталей контента"""
    queryset = Content.objects.filter(is_banned=False)
    permission_classes = (IsAuthenticatedOrReadOnly,)

    def get_object(self):
        kinopoisk_id = self.kwargs.get('pk')
        content = get_object_or_404(Content, kinopoisk_id=kinopoisk_id, is_banned=False)
        return content

    def get_serializer_class(self):
        if self.action == 'list':
            return ContentListSerializer
        elif self.action == 'retrieve':
            return ContentDetailSerializer

/urls

from django.urls import path
from rest_framework.routers import DefaultRouter

from . import views

router = DefaultRouter()
router.register(r'content', views.ContentViewSet)

urlpatterns = router.urls

Problem description:

When I am logged in and I want to get all of the content or specific content using a link:

http://127.0.0.1:8000/api/v1/content/?format=json

I succeed, but if so (if I log out of my account, this error does not occur:)

http://127.0.0.1:8000/api/v1/content/

Error:

Request Method: GET
Request URL: http://127.0.0.1:8000/api/v1/content/

Django Version: 4.1.6
Python Version: 3.10.5
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'django.contrib.postgres',
 'debug_toolbar',
 'django_elasticsearch_dsl',
 'rest_framework',
 'django_filters',
 'corsheaders',
 'rest_framework.authtoken',
 'djoser',
 'content']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'corsheaders.middleware.CorsMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'debug_toolbar.middleware.DebugToolbarMiddleware']



Traceback (most recent call last):
  File "/Users/nikita/PycharmProjects/MoviePlanetPlatform/venv/lib/python3.10/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
  File "/Users/nikita/PycharmProjects/MoviePlanetPlatform/venv/lib/python3.10/site-packages/django/core/handlers/base.py", line 220, in _get_response
    response = response.render()
  File "/Users/nikita/PycharmProjects/MoviePlanetPlatform/venv/lib/python3.10/site-packages/django/template/response.py", line 114, in render
    self.content = self.rendered_content
  File "/Users/nikita/PycharmProjects/MoviePlanetPlatform/venv/lib/python3.10/site-packages/rest_framework/response.py", line 70, in rendered_content
    ret = renderer.render(self.data, accepted_media_type, context)
  File "/Users/nikita/PycharmProjects/MoviePlanetPlatform/venv/lib/python3.10/site-packages/rest_framework/renderers.py", line 723, in render
    context = self.get_context(data, accepted_media_type, renderer_context)
  File "/Users/nikita/PycharmProjects/MoviePlanetPlatform/venv/lib/python3.10/site-packages/rest_framework/renderers.py", line 654, in get_context
    raw_data_post_form = self.get_raw_data_form(data, view, 'POST', request)
  File "/Users/nikita/PycharmProjects/MoviePlanetPlatform/venv/lib/python3.10/site-packages/rest_framework/renderers.py", line 553, in get_raw_data_form
    serializer = view.get_serializer()
  File "/Users/nikita/PycharmProjects/MoviePlanetPlatform/venv/lib/python3.10/site-packages/rest_framework/generics.py", line 110, in get_serializer
    return serializer_class(*args, **kwargs)

Exception Type: TypeError at /api/v1/content/
Exception Value: 'NoneType' object is not callable
Asked By: nnekkitt

||

Answers:

I think the problem is in here:

class ContentViewSet(LikedMixin, viewsets.ModelViewSet):
    ...

    def get_serializer_class(self):
        if self.action == 'list':
            return ContentListSerializer
        elif self.action == 'retrieve':
            return ContentDetailSerializer
        # only supports two actions

You can change the elif condition to else for handling all the action usecases or add another serializer when none of the conditions match.

Answered By: ruddra