SerializerMethodField not showing up - Django Rest Framework - django

So im trying to add a new field to my serializer by using SerializerMethodField(), but it is not showing. How come?
models.py:
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE) ##
content = models.TextField(max_length=400, blank=False)
date_posted = models.DateTimeField(auto_now_add=True)
#property
def username(self):
return self.author.username
def __int__(self):
return self.id
serializers.py:
class PostSerializer(serializers.ModelSerializer):
username = serializers.SerializerMethodField()
class Meta:
model = Post
fields = '__all__'
def get_username(self, obj):
return obj.username
#return obj.author.username dooesnt work either
view.py where i go to see the details of a post;
#api_view(['GET'])
def postDetail(request, pk):
post = Post.objects.get(id=pk)
serializer = PostSerializer(post, many=False)
return Response(serializer.data)
and this is what turns up, username field is missing:
{
"id": 2,
"content": "6",
"date_posted": "08/12/2020 18:33:55",
"author": 1
}

Related

django restframework object-level-validation

These are my models : Test, Skillarea, question
MODELS.PY :
class Test(BaseModel):
types = models.ManyToManyField(
TestType,
related_name='tests',
)
title = models.CharField(max_length=255)
summary = models.TextField()
def __str__(self):
return self.title
class SkillArea(BaseModel):
title = models.CharField(max_length=255)
test = models.ForeignKey('Test', on_delete=models.PROTECT, related_name='skill_areas')
questions = models.ManyToManyField(
'assessment.Question',
related_name='skill_areas',
)
def __str__(self):
return self.title
class Question(BaseModel):
question_text = models.TextField()
def get_absolute_url(self):
self.get_type_display()
def __str__(self):
return truncatewords(self.question_text, 7)
class TestType(BaseModel):
title = models.CharField(max_length=255)
def __str__(self):
return self.title
I want to have an updateserializer for updating, but the field "type" in the Test model, only can be updated if there is no question in Skillarea model which related to Test model( has the same id as updating test, in its test field)
I wrote these serializer and view but it doesnt know data['id'] which i used in validator and sends KeyError: 'id'
serializer.py :
class TestUpdateAPIViewSerializer(serializers.ModelSerializer):
def validate(self, data):
questions = SkillArea.objects.filter(test=data['id'], questions__isnull=False)
if questions.exists():
raise serializers.ValidationError("You may not edit type")
return data
class Meta:
model = Test
fields = (
'id',
'types',
'title',
'summary',
)
Views.py :
class TestUpdateAPIView(APIView):
def patch(self, request, pk):
test = Test.active_objects.get(pk=pk)
serializer = TestUpdateAPIViewSerializer(instance=test, partial=True, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.data, status=status.HTTP_400_BAD_REQUEST)
I think you can get the id from instance attribute.
class TestUpdateAPIViewSerializer(serializers.ModelSerializer):
def validate(self, data):
questions = SkillArea.objects.filter(test=self.instance.id, questions__isnull=False)
...

Creating an object in nested serializer giving an error of bad request "owner is required"?

This is my first time dealing with nested serializers and it gives me an error "owner" is required while creating a post. Why is that?
class CreatePostView(APIView):
permission_classes = [IsAuthenticated]
parser_classes = [MultiPartParser]
def post(self, request, *args):
user = request.user
data = request.data
data['owner'] = user
print(data)
serializer = PostSerializer(data=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)
PostSerializer:
class PostSerializer(serializers.ModelSerializer):
owner = UserSerializer()
comments = CommentSerializer(many=True, read_only=True)
class Meta:
model= Post
fields = ("id", "img", "posted_at", "caption", "owner", "comments")
Post model:
class Post(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name="posts")
img = models.ImageField(upload_to=post_img_url, default="default.jpg", max_length=200)
caption = models.CharField(max_length=100, null=True, blank=True)
posted_at = models.DateTimeField(auto_now_add=True, blank=True)
objects = models.Manager()
current_user_posts = CurrentUsersPosts.as_manager()
class Meta:
ordering = ("-posted_at",)
def __str__(self):
return f"{self.img} posted by {self.owner} at {self.posted_at}"
console:
<QueryDict: {'owner': [], 'img': [<InMemoryUploadedFile:
treeHouse.jpg (image/jpeg)>]}> Bad Request: /api/posts/upload/
[02/Aug/2022 11:14:01] "POST /api/posts/upload/ HTTP/1.1" 400 37
You should not modify request.data, do this instead:
if serializer.isvalid():
serializer.save(owner=request.user)

How to return upto date serializer data django rest framework

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

DRF Foreignkey serialization

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)

Django Rest Framework: How to associate the object with the user when posting the object

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)