Reputation: 980
I have this model:
class Comment(models.Model):
user = models.ForeignKey('users.User', on_delete=models.CASCADE, null=False)
post = models.ForeignKey('posts.Post', on_delete=models.CASCADE, null=False)
content = models.TextField(max_length=1000, null=False, blank=False)
def __str__(self):
"""Return the comment str."""
return "'{}'".format(self.content)
And its serializer looks like this:
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = '__all__'
So far so good when it comes to retrieve data, but when I want to create
a new Comment
, what would be the proper way to do so?
My view:
class CommentViewSet(viewsets.ModelViewSet):
def get_permissions(self):
permissions = []
if self.action == 'create':
permissions.append(IsAuthenticated)
return [p() for p in permissions]
def create(self, request):
"""Create a comment in some post."""
serializer = CommentSerializer(data=request.data)
if serializer.is_valid(): # False because user value is missing.
serializer.save()
return Response(serializer.data)
else:
return Response(serializer.errors)
That's ok, I tried by overriding validate_user
and letting it just pass
, but validation is still running when I call is_valid
. Also I tried to append request.user
to serializer.data
, but it's immutable.
How could I achieve this if I need to use request.user
? The value that I need to set to this field of the serializer is not in request.data
. (I don't think letting the front end send the user id would be a good idea).
Another solution I have in mind is by making user
ReadOnly in the serializer
, and then adding it in the Django object when I call to save()
. But I want to know what is the cleaner, restful way to do so with DRF.
Upvotes: 1
Views: 61
Reputation: 694
models.py :
class Comment(models.Model):
user = models.ForeignKey('users.User', on_delete=models.CASCADE, null=False)
post = models.ForeignKey('posts.Post', on_delete=models.CASCADE, null=False)
content = models.TextField(max_length=1000, null=False, blank=False)
def __str__(self):
"""Return the comment str."""
return "'{}'".format(self.content)
def get_username(self):
return self.user.get_username()
def get_post_title(self):
return self.post.title
# and so on ...
serializers.py :
class CommentSerializer(serializers.ModelSerializer):
username = serializers.SerializerMethodField()
post_title = serializers.SerializerMethodField()
class Meta:
model = Comment
fields = [
"id", "username", "post_title", "content"
]
def get_username(self, obj):
return obj.get_username()
def get_post_title(self, obj):
return obj.get_post_title()
views.py :
from rest_framework import generics
from rest_framework.permissions import IsAuthenticated
class CommentViewSet(generics.CreateAPIView):
model = Comment
queryset = Comment.objects.all()
serializer_class = CommentSerializer
permission_classes = [IsAuthenticated]
Upvotes: 1
Reputation: 477684
You should alter the serializer to use the context and patch the user field:
class CommentSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = Comment
fields = '__all__'
def create(self, validated_data):
validated_data['user'] = self.context['request'].user
return super().create(validated_data)
In the CommentViewSet
, you do not override create
, since the create
function is more complex, and will provide the context to the serializer:
class CommentViewSet(viewsets.ModelViewSet):
def get_permissions(self):
if self.action == 'create':
return [IsAuthenticated()]
return []
# no create
Upvotes: 1
Reputation: 88669
First, you have to set user as read_only
field, which will turn off the validation.
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = '__all__'
read_only_fields = ('user',)
Then, override the perform_create(...)
method of the viewset,
class CommentViewSet(viewsets.ModelViewSet):
def get_permissions(self):
permissions = []
if self.action == 'create':
permissions.append(IsAuthenticated)
return [p() for p in permissions]
def perform_create(self, serializer):
serializer.save(user=self.request.user)
Note:
You should remove the create(...)
method of the viewset since it is doing nothing "in your case"
Upvotes: 2