I've been stumped by this for a couple of hours now, and have read through a bunch of documentation and different tutorials, but I still do not know what I'm doing wrong.
I'm trying to make a POST request to create a Comment (my model, views, etc. defined below) and make an association with the User that is making the POST request, but I keep getting the error {"user":["This field is required."]}
I thought that adding
def perform_create(self, serializer):
serializer.save(user=self.request.user)
to my viewset would make this easy, but that doesn't appear to be working...
My models.py looks like:
import uuid
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth import get_user_model
from django.db import models
User = get_user_model()
class Comment(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.ForeignKey(User, on_delete=models.DO_NOTHING)
text = models.TextField(blank=False, null=False)
created = models.DateTimeField(auto_now_add=True)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.CharField(max_length=256)
content_object = GenericForeignKey('content_type', 'object_id')
serializer.py
from rest_framework import serializers
from .models import Comment
from organization.serializers import UserSerializer
from django.contrib.auth import get_user_model
User = get_user_model()
class CommentSerializer(serializers.ModelSerializer):
id = serializers.CharField(read_only=True, required=False)
user = UserSerializer(required=True)
text = serializers.CharField(required=True)
created = serializers.DateTimeField(read_only=True)
object_id = serializers.CharField(required=False)
class Meta:
model = Comment
fields = ('id', 'user', 'text', 'created', 'object_id')
read_only_fields = ('id', 'created')
views.py
from rest_framework import viewsets
from rest_framework import permissions
from .models import Comment
from .serializers import CommentSerializer
class CommentViewSet(viewsets.ModelViewSet):
queryset = Comment.objects.all()
serializer_class = CommentSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
return self.queryset.filter(object_id=self.kwargs['object_id'])
def perform_create(self, serializer):
serializer.save(user=self.request.user)
urls.py
from django.urls import include, path
from rest_framework import routers
from .views import CommentViewSet
router = routers.DefaultRouter()
router.register(r'(?P<content_type>[\w\-] )/(?P<object_id>[\w\-] )', CommentViewSet)
urlpatterns = [
path('', include(router.urls)),
]
CodePudding user response:
Ok - I got this working by overriding the create
method of CommentViewSet and simplifying my CommentSerializer
serializer.py
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = ('id', 'user', 'text', 'created', 'edited', 'object_id', 'content_type')
read_only_fields = ('id', 'created')
views.py
from rest_framework import viewsets
from rest_framework import permissions
from rest_framework import status
from rest_framework.response import Response
from django.contrib.contenttypes.models import ContentType
from .models import Comment
from .serializers import CommentSerializer
class CommentViewSet(viewsets.ModelViewSet):
queryset = Comment.objects.all()
serializer_class = CommentSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
return self.queryset.filter(object_id=self.kwargs['object_id'])
def create(self, request, **kwargs):
"""
Overrides the standard create method so that we can set User,
object_id and content_type based on requesting user and kwargs
passed in URL
"""
comment_data = self.request.data
# change request data so that it's mutable, otherwise this will raise
# a "This QueryDict instance is immutable." error
comment_data._mutable = True
# set the requesting user ID for the User ForeignKey
comment_data['user'] = self.request.user.id
comment_data['object_id'] = self.kwargs['object_id']
# pass kwarg from URL to `model` to get the corresponding object
content_type = ContentType.objects.get( model=self.kwargs['content_type'] )
# pass the ID from the ContentType object to `content_type`, expected
# by serializer
comment_data['content_type'] = content_type.id
serializer = CommentSerializer(data=comment_data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
CodePudding user response:
Serializer validation works before perform_create, change user field to read only and it works as expected.
class CommentSerializer(serializers.ModelSerializer):
id = serializers.CharField(read_only=True, required=False)
user = UserSerializer(read_only=True)
text = serializers.CharField(required=True)
created = serializers.DateTimeField(read_only=True)
object_id = serializers.CharField(required=False)
class Meta:
model = Comment
fields = ('id', 'user', 'text', 'created', 'object_id')
read_only_fields = ('id', 'created')