Django Rest Framework - Передача 'user' из ModelViewSet в сериализатор
Я уже несколько часов бьюсь над этим вопросом, перечитал кучу документации и различных руководств, но так и не понял, что я делаю не так.
Я пытаюсь сделать POST запрос для создания комментария (моя модель, представления и т.д. определены ниже) и создания ассоциации с пользователем, который делает POST запрос, но я продолжаю получать ошибку {"user":["This field is required."]}
Я думал, что добавление
def perform_create(self, serializer):
serializer.save(user=self.request.user)
в мой набор представлений упростило бы эту задачу, но, похоже, это не работает...
Мой models.py выглядит так:
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)),
]
Ок - я заставил это работать, переопределив create
метод CommentViewSet и упростив свой 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)
Валидация сериализатора работает перед perform_create, измените пользовательское поле на только для чтения и оно работает как ожидалось.
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')