I am facing an issue in DRF serializers. I have a model named Issue which has a foreign Key of User Model to save the user who has created the particular Issue. Now the Get request is working fine when I request to get the issues I get it perfectly fine with username who has created the issue, but when I do the post request I get the error on "created_by" field that "This field is requied" even though I am providing this field.
Following is my code:
Model
class Issues(models.Model):
created_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='issue_created_by')
title = models.CharField(max_length=225, default='', null=False)
details = models.CharField(max_length=1000, default='')
created_on = models.DateField(default=timezone.now)
tags = models.CharField(max_length=225, blank=True, null=True, default='')
Issue Serializer
class IssueSerializer(serializers.ModelSerializer):
created_by = UserSerializer()
class Meta:
model = Issues
fields = ['created_by', 'title', 'details', 'created_on', 'tags']
UserSerializer
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username', 'first_name', 'last_name', 'email', 'password']
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = super().create(validated_data)
user.set_password(validated_data['password'])
user.save()
return True
views.py
class IssueView(viewsets.ViewSet):
def create(self, request):
serialized_issues = IssueSerializer(data=request.data)
if serialized_issues.is_valid():
serialized_issues.save()
return Response({'message': 'Issue Created', 'status': status.HTTP_200_OK})
else:
return Response({'error': serialized_issues.errors, 'status': status.HTTP_400_BAD_REQUEST})
def list(self, request):
all_issues = Issues.objects.all()
serialized_issues = IssueSerializer(all_issues, many=True)
return Response(serialized_issues.data)
To work with a writeable nested serializer you need to overwrite the create method of the serializer. You can see the documentation [here]: https://www.django-rest-framework.org/api-guide/relations/#writable-nested-serializers
It seems you are creating a new user with every Issue created. It is quite unrealistic. You need to pass the request.user object to the serializer while creating a new issue. The updated code may be like
method in view
def create(self, request, *args, **kwargs):
created_by = request.user
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
serializer.save(created_by=created_by)
return Response(data=serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(data=serializer.errors, status=status.HTTP_400_BAD_REQUEST)```
and serializer file may be like
class IssueSerializer(serializers.ModelSerializer):
class Meta:
model = Issues
fields = ['title', 'details', 'created_on', 'tags']
Related
I'm unable to get current user inside serializer. I have passed context but still i get error like
"user": [
"This field is required."
]
#Serializer.py
class AddressSerializer(ModelSerializer):
class Meta:
model = Address
fields = "__all__"
def create(self, validated_data):
request = self.context["request"]
validated_data["user"] = request.user
return super().create(validated_data)
#Views.py
class AddAddress(APIView):
permission_classes = [IsAuthenticated]
def post(self, request):
print(request.user)
serializer = AddressSerializer(data=request.data, context={"request":request})
if serializer.is_valid():
serializer.save()
return Response(serializer.data, 200)
return Response(serializer.errors)
#Models.py
class Address(models.Model):
user = models.ForeignKey(Account, on_delete=models.CASCADE)
full_name = models.CharField(max_length=35)
email = models.EmailField(max_length=100)
phone = models.BigIntegerField()
address_line_1 = models.TextField(max_length=500)
address_line_2 = models.TextField(max_length=500)
zip_code = models.IntegerField()
city = models.CharField(max_length=20)
state = models.CharField(max_length=15)
country = models.CharField(max_length=15)
class Meta:
verbose_name_plural = "Address"
def __str__(self):
return self.full_name
I exactly don't know the problem behind this
You can do this with the save() method
class AddAddress(APIView):
permission_classes = [IsAuthenticated]
def post(self, request):
print(request.user)
serializer = AddressSerializer(data=request.data, context={"request":request})
if serializer.is_valid():
serializer.save(user=request.user)
return Response(serializer.data, 200)
return Response(serializer.errors)
Try this:
request = self.context.get('request', None)
instead of this request = self.context["request"]
It because user is required in serializer, mean that user data must be sent when post request.
From your code, you want to add the address according to the user who is logged in. So, user no need send by frontend. Try below:
class AddressSerializer(ModelSerializer):
class Meta:
model = Address
fields = "__all__"
extra_kwargs = {
"user": {"read_only": True}
}
def validate(self, attrs):
# make sure that you have passed request in context serializer
attrs["user"] = self.context["request"].user
return attrs
It is a foreign key. You need to assign the id to it, instead of the entire user.
validated_data["user"] = request.user.id
Event hen if the error persists, then the issue seems to be there is no "user" field in your validated_data. There is issue with the data you are sending in the request to the serializer. If it doesnt match the required fields, then validated data will be empty. Print and check your validated data, and also your request data.
currently when i send a get request i get what i want. But when i do a Post it throws an error saying {"user": ["This field is required."]} even though i put 'user' in read_only_fields.
heres the code:
serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username', 'email']
class SaleHistorySerializier(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = SaleHistory
fields =['id', 'user', 'product', 'date_bought']
read_only_fields = ('user',)
depth = 1
models.py
class SaleHistory(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='owner')
product = models.ForeignKey(SaleItems, on_delete=models.RESTRICT, default=None)
date_bought = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f' {self.product}, {self.user}, {self.id}'
api.py create part
class SaleHistoryVS(viewsets.ViewSet):
permission_classes = [permissions.IsAuthenticated]
def create(self, request):
serializer = SaleHistorySerializier(data=request.data, many=True)
if serializer.is_valid():
serializer.save(user = request.user)
return Response(serializer.data, status= status.HTTP_201_CREATED)
return Response(serializer.errors, status= status.HTTP_400_BAD_REQUEST)
how do i make it so that i get to create post request without having to user in my post data.
Replace user = UserSerializer() with user = UserSerializer(read_only=True)
I am trying to vote up comments in my serializer.
All seems to work well with the voting part of it.
However, the serializer returns old values rather than the updated values.
I know I am doing something wrong, just not sure how to put it all together to return the recent value through $ajax after the comment vote is recorded.
Note: When I refresh the page, I get the right vote count.
Here is my view.py that records the votes:
class CommentUpdateAPIView(DestroyModelMixin, generics.RetrieveUpdateAPIView):
queryset = Comment.objects.all()
serializer_class = CommentUpdateSerializer
permission_classes = [IsOwnerOrReadOnly]
def perform_update(self, serializer):
if self.request.user.is_authenticated:
serializer.save(user=self.request.user)
# For Comments Voting
def partial_update(self, request, *args, **kwargs):
if request.method == 'PATCH':
if self.request.user.is_authenticated:
comment = get_object_or_404(Comment, pk=kwargs['pk'])
posttype = self.request.data['type']
try:
if posttype == 'down':
if comment.votes.exists(request.user.id, 1):
comment.votes.delete(request.user.id)
else:
comment.votes.down(request.user.id)
elif posttype == 'up':
if comment.votes.exists(request.user.id, 0):
comment.votes.delete(request.user.id)
else:
comment.votes.up(request.user.id)
# Should return the latest, values in the database
# Unfortunately Only old value keep coming back...
return Response(CommentUpdateSerializer(comment).data)
except:
raise serializers.ValidationError({'detail': _('Something isn\'t right! Try again later')})
And my serializer:
class UserPublicSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = [
'username',
'first_name',
'last_name',
'is_superuser',
'is_staff'
]
class CommentSerializer(serializers.ModelSerializer):
user = UserPublicSerializer(read_only=True)
image = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Comment
fields = ('id', 'url', 'user', 'content', 'timestamp', 'updated_at', 'image', 'num_vote_down', 'num_vote_up')
def get_image(self, obj):
img_ = static("img/avatar.png")
if obj.user.photo_url:
img_ = obj.user.photo_url
return img_
class CommentUpdateSerializer(serializers.ModelSerializer):
user = UserPublicSerializer(read_only=True)
image = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Comment
fields = ('id', 'url', 'user', 'content', 'timestamp', 'updated_at', 'image', 'num_vote_down', 'num_vote_up')
read_only_fields = ('url', 'user')
Updated: Model:
class Comment(VoteModel, models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, default=1, null=True, blank=True, on_delete=models.SET_NULL)
url = models.URLField()
content = models.TextField()
num_vote = models.IntegerField(default=0)
#image = models.ImageField()
allow_annon = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now=False, auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.url
class Meta:
ordering = ['-created_at']
#property
def owner(self):
return self.user
What do I need to change to get the recent values after a vote is recorded?
you can use refresh_from_db() to update the values of an object.
So it would be:
comment.refresh_from_db()
return Response(CommentUpdateSerializer(comment).data)
Based on this comment
I can't save model with Foreignkey field.
Thanks to "azudo" problem solved. Solution below
For example I have simple models:
class User(AbstractUser):
class Meta:
pass
email_validator = EmailValidator()
username = models.CharField('Name', max_length=150, )
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
email = models.EmailField('Email', blank=True, unique=True, validators=[email_validator], )
...
class Package(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='packages')
description = models.CharField('Description', max_length=256, default='description')
weight = models.CharField('weight', max_length=256, default='weight')
...
View (the user is guaranteed to be in the request):
#api_view(["POST"])
def test(request):
data = request.data
data['user'] = User.objects.get(id=request.user.id)
serializer = PackageSerializer(data=data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data)
else:
return JsonResponse(serializer.errors)
My serializers:
class UserSerializer(ModelSerializer):
class Meta:
model = User
fields = '__all__'
class PackageSerializer(ModelSerializer):
class Meta:
model = Package
fields = (
'user', 'description', 'weight', 'dimensions', 'estimated_shipping_cost', 'deliver_to_date')
def to_representation(self, instance):
self.fields['user'] = UserSerializer(many=False, read_only=True)
self.fields['where_from'] = LocationSerializer(many=False, read_only=True)
self.fields['destination'] = LocationSerializer(many=False, read_only=True)
return super().to_representation(instance)
def create(self, validated_data):
user = User.objects.get(validated_data.pop('user'))
package = Package.objects.create(user=user, **validated_data)
return package
json in request:
{
"description": "Some package",
"weight": "12",
}
So, I'have user in database, and want create package for him. But in overridden create in PackageSerializer, validated_data doesn't have user. Please explain what I'm doing wrong.
Versions of django and drf:
django==2.2.4
djangorestframework==3.10.2
Solution:
Serializer:
class PackageSerializer(ModelSerializer):
user = UserSerializer(many=False, read_only=True)
class Meta:
model = Package
fields = (
'user', 'description', 'weight', 'dimensions', 'estimated_shipping_cost', 'deliver_to_date')
def create(self, validated_data):
user = User.objects.get(validated_data.pop('user'))
package = Package.objects.create(user=user)
return package
View:
#api_view(["POST"])
def create_package(request):
data = request.data
serializer = PackageSerializer(data=data)
if serializer.is_valid():
serializer.save(user=request.user)
return JsonResponse(serializer.data)
else:
return JsonResponse(serializer.errors)
DRF will ignore included fields that are marked as read-only so the caller cannot include read-only data. If you want to include additional attributes simply pass them as keyword args to save:
https://www.django-rest-framework.org/api-guide/serializers/#passing-additional-attributes-to-save
e.g.
#api_view(["POST"])
def test(request):
data = request.data
serializer = PackageSerializer(data=data)
if serializer.is_valid():
serializer.save(user=request.user)
return JsonResponse(serializer.data)
else:
return JsonResponse(serializer.errors)
I'm new to creating REST API so I might misunderstand something.
I'm creating REST API using Django Rest Framework. And I'm trying to create an object and send it from my mobile app.
However, API returns 400. I think it still cannot associate the object with the request user and I'm wondering how to do it.
models.py
class Item(models.Model):
item_name = models.CharField()
created_at = models.DateTimeField(auto_now_add=True)
created_by = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
serializers.py
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = ('item_name', 'created_by')
and views.py
class ListItems(generics.ListCreateAPIView):
queryset = Item.objects.all()
serializer_class = ItemSerializer
What I want to know is how to associate the object with the request user when posting the object like as we do like
if form.is_valid():
item = form.save(commit=False)
item.created_by = request.user
item.save()
I think the easiest approach is like this:
class ItemSerializer(serializers.ModelSerializer):
created_by = serializers.HiddenField(
default=serializers.CurrentUserDefault()
)
Reference can be found here
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = ('item_name',)
class ListItems(generics.ListCreateAPIView):
...
def perform_create(self, serializer):
serializer.save(created_by=self.request.user)
you can do this way
One of the possible way to overwrite serializer_create method. As user is not associated with request.data first we need to make sure, this is write_only field and also need to assign current user from modelSerializer's self.context.request.user. Following addition should solve the problem.
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = ('item_name', 'created_by')
extra_kwargs = {'created_by': {'write_only': True}}
def create(self, validated_data):
item = Item(
item_name=validated_data['item_name'],
created_by=self.context.request.user
)
item.save()
return item
Reference link
It works for me
models.py
class Category(models.Model):
name = models.CharField('Category', max_length=200, unique=True, help_text='Name of the category')
slug = models.SlugField('Slug', max_length=100, db_index=True, unique=True, help_text='Name of the category in format URL')
def __str__(self):
return (self.name)
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(Category, self).save(*args, **kwargs)
serializers.py
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = [
'id', 'name', 'slug'
]
read_only_fields = [
'slug',
]
Finally, I get the user in the view, before to save the post.
views.py
class CategoryList(APIView):te a new category instance.
permission_classes = (IsAuthenticatedOrReadOnly,)
def get(self, request, format=None):
categories = Category.objects.all()
serializer = CategorySerializer(categories, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
def post(self, request, format=None):
serializer = CategorySerializer(data=request.data)
if serializer.is_valid():
serializer.save(created_by=self.request.user)
Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)