APIView post function not adding to database

Question:

I have my follow model:

class Follow(models.Model):
    user = models.ForeignKey(
        "User", related_name="follower", on_delete=models.CASCADE)
    following_user = models.ForeignKey(
        "User", related_name="following", blank=True, on_delete=models.CASCADE)
    date_followed = models.DateTimeField(editable=False, default=timezone.now)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['user', 'following_user'],  name="unique_followers")
        ]

        ordering = ["-date_followed"]

    def __str__(self):
        return f"{self.user.username} follows {self.following_user.username}"

and serializer:

class FollowSerializer(serializers.ModelSerializer):

    class Meta:
        model = Follow
        fields = ['user', 'following_user', 'date_followed']

Then in my views I have an APIView that creates the follow:

class FollowingView(APIView):
    permission_class = [permissions.IsAuthenticated]
    queryset = Follow.objects.all()
    serializer_class = FollowSerializer

    def post(self, request):
        user = request.data.get('user')
        following_user = request.data.get('to_user')

        try:
            follow = Follow.objects.create(
                user=user, following_user=following_user)
            follow.save()

            serializer = FollowSerializer(follow)

            print(serializer.data)

            def __str__(self):
                return f"{self.request.username} follows {self.following_user_id.username}"

            return Response(serializer.data, status=status.HTTP_201_CREATED)
        except:
            return Response(status=status.HTTP_400_BAD_REQUEST)

Not sure what is wrong. I am able to print the params but seems like the create function isn’t going through.

Appreciate any help!

Asked By: jdez

||

Answers:

You need to remove .save() method which used with object.create.

object.create used for save data object in table so, no need to .save()

follow = Follow.objects.create(user=user, following_user=following_user)
serializer = FollowSerializer(follow)
print(serializer.data)

You can also save data and serialize it with APIView() approach like this

def post(self, request):
    serializer = FollowSerializer(data=request.data)
    print(serializer.is_valid()) ## for debug
    print(serializer.errors) ## for debug
    if serializer.is_valid():
        serializer.save()
        return Response({'msg':'Data Created'}, status=status.HTTP_201_CREATED)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

First set read_only=True to user & following_user fields if it’s not getting data from client.

class FollowSerializer(serializers.ModelSerializer):
    user = serializers.PrimaryKeyRelatedField(many=False, read_only=True)
    following_user = serializers.PrimaryKeyRelatedField(many=False, read_only=True)
    class Meta:
        model = Follow
        fields = ['user', 'following_user', 'date_followed']

and inside your view just validate your serializer and save it with related fields

class FollowingView(APIView):
    permission_class = [permissions.IsAuthenticated]
    queryset = Follow.objects.all()
    serializer_class = FollowSerializer

    def post(self, request):
        user = request.data.get('user')
        following_user = request.data.get('to_user') # check if it's correct

        try:
            user = User.objects.get(id=user)
            following_user = User.objects.get(id=following_user)
            serializer = FollowSerializer(request.data)
            if serializer.is_valid():
               serializer.save(
                 user=user,
                 following_user=following_user
               )

            def __str__(self):
                return f"{self.request.username} follows {self.following_user_id.username}"

            return Response(serializer.data, status=status.HTTP_201_CREATED)
        except:
            return Response(status=status.HTTP_400_BAD_REQUEST)
Answered By: Ankit Tiwari

First problem is in this line:

following_user = request.data.get('to_user')

There is no such to_user field in your serializer, thus the value you are fetching from request is None. Replace the variable accordingly.

following_user = request.data.get('following_user')

Secondly, are trying to create your Follow objects directly with IDs instead of user instances, just fetch User instances before trying to create the object.

from rest_framework import permissions
from core.models import Follow
from core.api.serializers import FollowSerializer
from django.contrib.auth import get_user_model

class FollowingView(APIView):
    permission_class = [permissions.IsAuthenticated]
    queryset = Follow.objects.all()
    serializer_class = FollowSerializer

    def post(self, request):
        user_id = request.data.get('user')
        following_user_id = request.data.get('following_user')

        user = get_object_or_404(get_user_model(), id=user_id)
        following_user = get_object_or_404(get_user_model(), id=following_user_id)

        try:
            follow = Follow.objects.create(
            user=user, following_user=following_user)
            follow.save()
            serializer = FollowSerializer(follow)
            print(serializer.data)

            def __str__(self):
                return f"{self.request.username} follows {self.following_user_id.username}"

            return Response(serializer.data, status=status.HTTP_201_CREATED)
        except:
            return Response(status=status.HTTP_400_BAD_REQUEST)

Edit: Refer to @Ankit Tiwari answer on how to properly use the serializer to validate data

Answered By: Niko