Search code examples
pythondjangodjango-rest-frameworkback

Making a that enables 'GET' without token but not posts without tokens


I recently started working with Django backend development, so I'm still struggling with some features. This time, I've encountered a problem that has been quite challenging, and I couldn't find any helpful answers on the web. I'm creating a view that supports both 'GET' and 'POST' requests. The issue I'm facing is that while I need a Bearer token for the 'POST' request, I don't want to require a token for the 'GET' request. Initially, I considered creating a separate view just for the 'GET' request, but that seemed redundant. Then, I came across the 'IsAuthenticatedOrReadOnly' permission class in the Django Rest Framework documentation. I thought this would solve my problem, but even after implementing this verification, the 'GET' endpoint still asks for a token. Could someone please provide me with some insights?

Here's my view:

from rest_framework.views import Request, Response, status, APIView
from .serializers import MovieSerializer
from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from movies.models import Movie


class MovieView(APIView):
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticatedOrReadOnly]

    def post(self, request: Request) -> Response:
        serializer = MovieSerializer(data=request.data)

        serializer.is_valid(raise_exception=True)

        serializer.save(user = request.user)

        return Response(serializer.data, status=status.HTTP_201_CREATED)
    
    def get(self, request: Request) -> Response:
        movies = Movie.objects.all()
        serializer = MovieSerializer(movies, many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)

...and in case you need... here's my model:

from django.db import models

# Create your models here.

class MovieRating(models.TextChoices):
    G = 'G'
    PG = 'PG'
    PG13 = 'PG-13'
    R = 'R'
    NC17 = 'NC-17'

class Movie(models.Model):
    title = models.CharField(max_length=127)
    duration = models.CharField(max_length=127, blank=True, null=True, default=None)
    rating = models.CharField(max_length=20, choices=MovieRating.choices, default=MovieRating.G)
    synopsis = models.TextField(blank=True, null=True, default=None)

    user = models.ForeignKey(
        "users.User",
        on_delete=models.CASCADE,
        related_name="movies",
        default=None
    )


Solution

  • You can override the get_permissions class based on the request made. The example below works with ModelViewSet.

    def get_permissions(self):
        if self.action in ['list', 'retrieve']:
            # Get requests
            return (permissions.AllowAny(),)
        else:
            # Any other request
            return (permissions.IsAuthenticated(),)