i am trying to create a backend for blog website. I created a comment model and i have associated with blog post it. When I list the comments, I can also list the answers that have been made to the comment. But there is a problem that even though it appears in the comment as an answer, it also looks like it was the main comment again. How can I solve this. I would appreciate it if someone could help. I may have mistakes in English, I hope I was able to tell you what I wanted to tell you. Thanks in advance.
https://i.stack.imgur.com/HcEoT.png
Comment Model:
class Comments(models.Model):
class Meta:
verbose_name = 'Yorum'
verbose_name_plural = 'Yorumlar'
user = models.ForeignKey(User, verbose_name='Yorum sahibi', on_delete=models.CASCADE)
post = models.ForeignKey(Post, verbose_name='Yorum yapılacak Post', on_delete=models.CASCADE,related_name='comments')
parent_id = models.ForeignKey('self', verbose_name='Parent Yorum', related_name='subcomments',on_delete=models.CASCADE, null=True, blank=True)
comment = models.TextField(verbose_name='Yorum')
[enter image description here][1]created_at = models.DateTimeField(auto_now_add=True, blank=True)
updated_at = models.DateTimeField(auto_now=True, blank=True)
def __str__(self):
return self.user.first_name + " " + self.user.last_name + " | " + self.post.title
Comment Serializer:
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comments
fields = '__all__'
def get_fields(self):
fields = super(CommentSerializer, self).get_fields()
fields['subcomments'] = CommentSerializer(many=True, read_only=True)
return fields
Post Serializer
class PostSerializer(serializers.ModelSerializer):
tag = serializers.StringRelatedField(many=True, read_only=True)
comments = CommentSerializer(many=True, read_only=True)
author = AuthorSerializer(read_only=True)
category = CategorySerializer2(read_only=True)
class Meta:
model = Post
fields = '__all__'
First Option
You can use a nested serializer to solve your problem
class CommentSerializer(serializers.ModelSerializer):
parent_id = serializers.PrimaryKeyRelatedField()
class Meta:
model = Comment
fields = '__all__'
def get_related_field(self, model_field):
return CommentSerializer()
Second Option
You can use a library called djangorestframework-recursive for your use case
First, install the library
pip install djangorestframework-recursive
In your serializer file
from rest_framework import serializers
from rest_framework_recursive.fields import RecursiveField
class CommentSerializer(serializers.ModelSerializer):
parent_id = serializers.ListField(child=RecursiveField())
class Meta:
model = Comments
fields = '__all__'
Related
I am new to this tech, while working on django project i got some issues when i try to serialize Ticket's account.profile_pic
models.py
class Account(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE
profile_pic = models.ImageField(upload_to='images/profile_pics/', blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
class Ticket(models.Model):
author = models.ForeignKey(Account, on_delete=models.CASCADE)
descr = models.TextField(blank=False, null=False)
likes = models.ManyToManyField(User)
serializers.py
class DetailedTicketSerializer(serializers.ModelSerializer):
# Error occurs on below line: No file associated with ImageField
author_profile_pic = serializers.ReadOnlyField(source='author.profile_pic')
author_username = serializers.ReadOnlyField(source='author.user.username')
class Meta:
model = Ticket
fields = ['id', 'author_profile_pic', 'author_username', 'likes', 'descr']
Anyone knows how do i serialize Account.profile_pic's url???
serialize the account class. in your ticketserializer call the account serializer.
Here an example:
class
HobbySerializer(serializers.ModelSerializer):
class Meta:
model = Hobby
fields = '__all__'
class ProfileSerializer(serializers.ModelSerializer):
user_hobby = HobbySerializer(many=True)
class Meta:
model = Profile
fields = '__all__'
I have 3 tables on data base: News, CustomUser(not important) and Comment:
class News(models.Model):
date = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=200)
text = models.TextField()
image = models.ImageField(upload_to='news_images/', blank=True)
author = models.ForeignKey(CustomUser, on_delete=models.SET_DEFAULT, blank=True, default=1)
tags = models.ManyToManyField(Tags, blank=True)
published = models.BooleanField(default=False)
comments = models.ManyToManyField(
CustomUser,
related_name='author',
through='Comments.Comment',
blank=True,
)
class Comment(models.Model):
date = models.DateTimeField(auto_now_add=True)
text = models.TextField()
author = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, null=True)
event = models.ForeignKey(News, on_delete=models.CASCADE, parent_link=True)
As you can see, the comment is a custom many-to-many model, that links Users and News, and adds some extra data (text of comment itself).
I using DRF, so I need to serialize it. I need news serializer to include list of comments with all their fields(like author fields as you can see below on the screenshot).But when i getting one of the news I see that:
{"id":2,"author":{"username":"Admin","email":"alexsiid29#gmail.com","first_name":"","last_name":""},"tags":[],"comments":[1],"date":"15.08.2021 10:10","title":"Тест2","text":"тестовая 2","image":"http://127.0.0.1:8000/news_images/%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9.png","published":true}
Comments: "comments":[1]
And when I printing during the view-function execution:
self.object = News.objects.filter(pk=pk).first()
print(self.object.comments.all())
I getting this: <QuerySet [<CustomUser: Admin>]>
So, instead of a normal representation of comments, I get the authors ID.
There are my serializers:
class CommentSerializer(serializers.ModelSerializer):
author = CustomUserSerializer(source='author.id')
event = NewsSerializer(source='event.id')
class NewsSerializer(serializers.ModelSerializer):
author = CustomUserSerializer(read_only=True)
tags = TagSerializer(required=False, allow_null=True, many=True)
comments = serializers.PrimaryKeyRelatedField(allow_null=True, many=True, read_only=True)
So, how can I serialize these tables, or maybe organize them in a different way?
P.S.
I also tried StringRelatedField instead of PrimaryRelatedField, but it returns author's name.
You have typo in your code: comments in News model must be linked with Comment model, not with CustomUser. That's why you get <QuerySet [<CustomUser: Admin>]>.
To solve "I need news serializer to include list of comments with all their fields", I'd do the following:
class CommentSerializer(serializers.ModelSerializer):
author = CustomUserSerializer()
class Meta:
model = Comment
exclude = ('event',) # in your particular case
class NewsSerializer(serializers.ModelSerializer):
author = CustomUserSerializer(read_only=True)
tags = TagSerializer(required=False, allow_null=True, many=True)
comments = CommentSerializer(many=True)
class Meta:
model = News
fields = "__all__"
I advise you to exclude event in CommentSerializer because you'll have this info in your NewsSerializer so it won't be repeated for each comment.
in models.py:
class Post(models.Model):
body = models.TextField(max_length=10000)
date = models.DateTimeField(auto_now_add=True, blank=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
liked_by = models.ManyToManyField(User, blank=True, related_name='liked_by')
class Meta:
ordering = ['-date']
in serializers.py:
class PostSerializer(serializers.ModelSerializer):
user = UserSerializers()
class Meta:
model = Post
fields = ('body','date','user')
how to count likes of a single post? and also show which user liked the post.
class PostSerializer(serializers.ModelSerializer):
user = UserSerializers()
total_likes = serilaizers.SerializerMethodField()
liked_by = UserSerializers(many=True)
class Meta:
model = Post
fields = ('body','date','user', 'total_likes', 'liked_by')
def get_total_likes(self, instance):
return instance.liked_by.count()
You need to change class Post manyToMany relation adding "through=".
liked_by = models.ManyToManyField(User, through='liked_users', blank=True, related_name='liked_by')
Create manually model LikedUsers:
class LikedUsers(models.Model):
liked_count = models.IntegerField(default=0)
post = models.ForeignKey(Post, related_name='likedusers', on_delete=models.CASCADE)
user = models.ForeignKey(User, related_name='likedusers', on_delete=models.CASCADE)
Get access to likes (post.likedusers[0].liked_count, ... next users)
You can get the total likes of a post using SerializerMethodField.
from rest_framework.fields import SerializerMethodField
class PostSerializer(serializers.ModelSerializer):
user = UserSerializers()
total_likes = SerializerMethodField()
class Meta:
model = Post
fields = ('body','date','user')
def get_total_likes(self, instance):
return instance.liked_by.all().count()
Your liked_by field is just referencing to User model, but not to User model and you have only who liked your post but likes quantity. So you can query certain post and count User quantity, but I suggest to have separate filed of likes count
Assuming im using the default django model, a Post model (code below) and a SavedPost model that links a User to a Post (if the certain user with the certain post exists then that post is saved for that user) and a Follower model that links 2 user (similar to SavedPost).
What im trying to do: An API that for a user, they get all posts for the users they follow, in addition each of these posts has an extra 'field' to say if that post is saved or not.
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post_type = models.CharField(max_length=1, choices=[('B', 'Blog'), ('V', 'Video')], default='B')
file_path = models.URLField(null=True)
title = models.CharField(max_length=255)
description = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class SavedPost(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
# A user can save a post only once.
unique_together = ('user', 'post')
class Follower(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="user")
follower = models.ForeignKey(User, on_delete=models.CASCADE, related_name="follower")
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
# A user can follow another user only once
unique_together = ('user', 'follower')
Post serilializer:
class PostSerializer(serializers.ModelSerializer):
"""
Nested serializer for post using SimpleUser and Kingdom.
"""
class Meta:
model = Post
fields = ('id', 'user', 'post_type', 'file_path',
'title', 'description', 'created_at', 'updated_at')
def to_representation(self, instance):
data = super().to_representation(instance)
data['user'] = UserSerializer(
User.objects.get(pk=data['user'])).data
return data
API View:
#permission_classes([IsAuthenticated,])
#api_view(['GET'])
def get_following(request):
user = request.user
following = Follower.objects.filter(follower=user).values('user')
# saved_posts = SavedPost.objects.filter(user=user, post__user__in=following).order_by('-post__created_at')
posts = Post.objects.filter(user__in=following).order_by('-created_at')
serializer = PostSerializer(posts, many=True, context={'request': request})
return JsonResponse(serializer.data, safe=False)
So far with the view I made I can get all the posts that the request.user follows but it doesnt say if they are saved or not. I am looking for say 'is_saved' boolean on post to say if that post is saved for that user or not.
Any help/method to do this appreciated. Thank you.
Use serializers.SerializerMethodField as
class PostSerializer(serializers.ModelSerializer):
is_saved = serializers.SerializerMethodField()
def get_is_saved(self, post_instance):
return SavedPost.objects.filter(user=post_instance.user, post=post_instance).exists()
class Meta:
model = Post
fields = ['id', 'user', 'post_type', 'file_path',
'title', 'description', 'created_at', 'updated_at', 'is_saved']
def to_representation(self, instance):
data = super().to_representation(instance)
data['user'] = UserSerializer(
User.objects.get(pk=data['user'])).data
return data
First of all, just to be clear, I will be defining the related_name option for the ForeignKeys in SavedPost - it's up to you to decide whether to implement this or not:
class SavedPost(models.Model):
user = models.ForeignKey(User, related_name="saved", on_delete=models.CASCADE)
post = models.ForeignKey(Post, related_name="saved", on_delete=models.CASCADE)
...
Now, in your PostSerializer, you could add this field (remember to add it to the fields variable in the Meta inner class - that is if you're using ModelSerializer):
class PostSerializer(serializers.ModelSerializer):
saved = SavedPostSerializer(many=True)
...
To finish it off, define your SavedPostSerializer - above PostSerializer, if in the same file/module:
class SavedPostSerializer(serializers.ModelSerializer):
class Meta:
model = SavedPost
fields = "__all__"
With this, your json should have a nested field with the saved key containing an array of SavedPosts, if there are any related to the Posts retrieved.
I have a pair of parent/children relation models like:
class Post(models.Model):
title = models.TextField(null=True)
content = models.TextField(null=True)
author = models.TextField(null=True)
created_time = models.DateTimeField(null=True)
class Comment(models.Model):
content = models.TextField(null=True)
created_time = models.DateTimeField(null=True)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
and the serializers are like:
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = '__all__'
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = '__all__'
and finally views:
class PostView(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
class CommentView(viewsets.ModelViewSet):
queryset = Comment.objects.all()
serializer_class = CommentSerializer
Now I want to created an API that returns a list of Posts, in which each Post will contain two additional fields, one be all_comments, and the other will be latest_comment. I understand this could be easily done in SQL using JOINs. I am new to Django. I wonder if there's any easy way to do it in Django. Thanks.
Hope this config works for you :)
class CommentPostSerializer(serializers.ModelSerializer): # New Serializer class
class Meta:
model = Comment
exclude = ('post',)
class PostSerializer(serializers.ModelSerializer):
all_comments = CommentPostSerializer(read_only=True, many=True, source='comment_set')
latest_comment = serializers.SerializerMethodField()
def get_latest_comment(self, post):
latest_comment = post.comment_set.last()
return CommentPostSerializer(latest_comment).data
class Meta:
model = Post
fields = '__all__'