I have 2 model. And the two models are connected to the ManyToManyField.
models.py
class PostModel(models.Model):
id = models.AutoField(primary_key=True, null=False)
title = models.TextField()
comments = models.ManyToManyField('CommentModel')
class CommentModel(models.Model):
id = models.AutoField(primary_key=True, null=False)
post_id = models.ForeignKey(Post, on_delete=models.CASCADE)
body = models.TextField()
and serializers.py
class CommentModel_serializer(serializers.ModelSerializer):
class Meta:
model = MainCommentModel
fields = '__all__'
class PostModel_serializer(serializers.ModelSerializer):
comment = MainCommentModel_serializer(many=True, allow_null=True, read_only=True)
class Meta:
model = MainModel
fields = '__all__'
and views.py
#api_view(['GET'])
def getPost(request, pk):
post = PostModel.objects.filter(id=pk).first()
comment_list = CommentModel.objects.filter(post_id=post.id)
for i in comments_list:
post.comments.add(i)
serializer = PostModel_serializer(post, many=True)
return Response(serializer.data)
There is an error when I make a request.
'PostModel' object is not iterable
and The trackback points here.
return Response(serializer.data)
I tried to modify a lot of code but I can't find solutions.
Please tell me where and how it went wrong.
Referring from this thread, you should remove many=True in PostModel_serializer.
Also it should be comment_list not comments_list.
#api_view(['GET'])
def getPost(request, pk):
post = PostModel.objects.filter(id=pk).first()
comment_list = CommentModel.objects.filter(post_id=post.id)
for i in comment_list:
post.comments.add(i)
serializer = PostModel_serializer(post)
return Response(serializer.data)
I think you did wrong while creating ManyToManyField().
Instead of this:
comments = models.ManyToManyField('CommentModel') #Here you made mistake. you should not add single quote to CommentModel. I think that's why you got that error
Try this:
comments = models.ManyToManyField(CommentModel)
Related
I have next serializers:
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = '__all__'
class PostSerializer(serializers.ModelSerializer):
category = CategorySerializer()
class Meta:
model = Post
fields = ['id', 'title', 'text', 'date', 'category']
And here is my view:
#api_view(['POST'])
def create_post(request):
serializer = PostSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
else:
return Response(serializer.errors)
return Response(serializer.data)
I want to create new Post object, but when I pass an category id at form it does not work, it is not saving my object. I tried to replace create method at my PostSerializer, to this:
def create(self, validated_data):
category_id = validated_data.pop('category')
post = Post.objects.create(**validated_data, category=category_id)
return post
but this dont work. Using postman formdata it is saying, that category field is required despite I filled it.
Here is my models:
class Category(models.Model):
name = models.CharField(max_length=512)
def __str__(self):
return self.name
class Post(models.Model):
title = models.CharField(max_length=512)
text = models.TextField()
date = models.DateTimeField(auto_now_add=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='category')
You need a category object not just an id, so try this
#api_view(['POST'])
def create_post(request):
category_id = request.data['category'] # or however you are sending the id
serializer = PostSerializer(data=request.data)
if serializer.is_valid():
category = Category.objects.get(id=category_id)
serializer.save(category=category)
else:
return Response(serializer.errors)
return Response(serializer.data)
or you can do something similar in the create method of the serializer
I have a comment field for every blog post. I want to pass Comment.objects.all() from Views.py to ModelSerializer def get_comments(self, obj) to reduce the number of sql queries. As I am serializing a list of blog posts
Views.py
class BlogViewSet(ModelViewSet):
queryset = Blog.objects.all().annotate(
author_name=F('author__username')
)
serializer_class = BlogSerializer
permission_classes = [IsOwnerOrReadOnly]
def list(self, request):
return Response({'blogs': BlogSerializer(self.queryset, many=True).data})
Serializers.py
class BlogSerializer(ModelSerializer):
author_name = serializers.CharField(read_only=True)
comments = SerializerMethodField()
class Meta:
model = Blog
fields = ('title_text', 'main_text', 'datepublished', 'author_name', 'id', 'comments')
def get_comments(self, obj):
# filter comment
comment_object = Comment.objects.filter(post_id=obj.id)
comments = CommentSerializer(comment_object, many=True).data
return comments
You don't have to pass anything from the View.
First, you have to change the comments field in your BlogSerializer.
class CommentSerializer(ModelSerializer):
# This serializer should have all the details of your comments.
....
class Meta:
model = Comment
fields = "__all__" # Or whatever fields you want to set.
class BlogSerializer(ModelSerializer):
author_name = serializers.CharField(read_only=True)
comments = CommentSerializer(many=True, read_only=True) # I am not sure of the source of your comment reverse manager name
class Meta:
model = Blog
fields = ('title_text', 'main_text', 'datepublished', 'author_name', 'id', 'comments')
Second, you have to make a small change to your view's queryset in order to reduce the number of queries sent to the database by using prefetch_related
class BlogViewSet(ModelViewSet):
queryset = Blog.objects.prefetch_related('comments').all().annotate(
author_name=F('author__username')
)
serializer_class = BlogSerializer
permission_classes = [IsOwnerOrReadOnly]
def list(self, request):
return Response({'blogs': BlogSerializer(self.get_queryset(), many=True).data})
I assumed in the code snippets that you didn't set a related_name on your Blog ForeignKey for the Comment model, so by default, its related manager on Blog will be comment_set
Update
Your models should look like this, in order for this solution to work
class Comment(models.Model):
...
blog = models.ForeignKey('Blog', on_delete=models.CASCADE, related_name='comments')
...
class Blog(models.Model):
# comment = models.ForeignKey(Comment, on_delete=models.CASCADE, null=True)
# this foreign key shouldn't be here, remove it.
....
Do the changes on Serializer and View
# in BlogSerializer
comments = CommentSerializer(many=True, read_only=True)
# In BlogViewSet
queryset = Blog.objects.prefetch_related('comments').all().annotate(
author_name=F('author__username')
)
id field and name field not showing in result.
in models.py:
class Group(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=50)
admin = models.ForeignKey(User, on_delete=models.CASCADE)
member = models.ManyToManyField(User, related_name='groups_user')
def __str__(self):
return self.name
in serializers.py:
class SimpleUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id','first_name', 'last_name')
class GroupSerializer(serializers.Serializer):
admin = SimpleUserSerializer()
class Meta:
model = Group
fields = ('id','name','admin')
views.py:
#api_view(['GET'])
#permission_classes((IsAuthenticated,))
def getSomeGroup(request):
allGroup = Group.objects.all().count()
randomGroupId = random.sample(range(allGroup), 3)
randomGroup = Group.objects.filter(id__in=randomGroupId)
serializer = GroupSerializer(randomGroup, many=True)
#print(serializer)
return Response(serializer.data)
the result comes like this:
[{"admin":{"id":1,"first_name":"asif","last_name":""}},{"admin":{"id":3,"first_name":"Test2","last_name":"lastname"}},{"admin":{"id":3,"first_name":"Test2","last_name":"lastname"}}]
why id and name field not showing?
class SimpleUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
First try to access all admin
#api_view(['GET'])
#permission_classes(IsAuthenticated)
def getSomeGroup(request):
randomGroup = Group.objects.all()
serializer = GroupSerializer(randomGroup, many=True)
return Response(serializer.data)
If that works there may be issue in your these two line
The Issue may be in these two lines
allGroup = Group.objects.all().count()
randomGroupId = random.sample(range(allGroup), 3)
Modify serializers.py:
class GroupSerializer(serializers.ModelSerializer):
I have such a case:
I have a WorkOrder class:
class WorkOrder(models.Model):
workorder_num = models.CharField(max_length=64, help_text="workorder number")
name = models.CharField(max_length=32, help_text="name")
content = models.TextField(help_text="content")
And I also have a WorkOrderComment class:
class WorkOrderComment(models.Model):
"""
comment
"""
workorder = models.ForeignKey(WorkOrder, help_text="belong to which order" )
comment_user = models.OneToOneField(User, help_text="comment user")
content = models.CharField(max_length=256, help_text="content")
So, there is a requirement, I want to list the workorder comments, so I write the serializers and views:
serializer:
class WorkOrderCommentSerializer(ModelSerializer):
class Meta:
model = WorkOrderComment
fields = "__all__"
view:
class WorkOrderCommentListAPIView(ListAPIView):
serializer_class = WorkOrderCommentSerializer
permission_classes = []
queryset = WorkOrderComment.objects.filter()
But if I list workorder comment, you know it will list all the comments, no organization.
I want to through workorder to get its comments how to do with that?
You can use the nested-relationships to do that.
You do not need the WorkOrderCommentListAPIView.
You can in your WorkOrderCommentSerializer:
class WorkOrderCommentSerializer(ModelSerializer):
comments = WorkOrderCommentSerializer(many=True, read_only=True)
class Meta:
model = WorkOrderComment
fields = "__all__"
Then the access WorkOrderCommentSerializer you can get what you want.
I have taken a look around, and I didn't find an answer to this question.
Serializers.py
class PostSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Post
fields = ['title', 'body', 'comments', 'user', 'date']
class CommentSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Comment
fields = ['body', 'user', 'date']
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ['id', 'user']
Models.py
class Post(models.Model):
# Children
comments = models.ManyToManyField('Comment', blank=True)
# Content
title = models.TextField(default="-->Title here<--")
body = models.TextField(default="-->Body here<--")
# About
user = models.ForeignKey(User)
date = models.DateTimeField(auto_now=True)
object_id = models.PositiveIntegerField(default=0)
content_type = models.ForeignKey(ContentType, default=0)
content_object = fields.GenericForeignKey()
def __str__(self):
return str(self.title)
class Comment(models.Model):
comments = models.ManyToManyField('Comment', blank=True)
# Content
body = models.TextField(default="-->Body here<--")
# About
user = models.ForeignKey(User)
date = models.DateTimeField(auto_now=True)
object_id = models.PositiveIntegerField(default=0)
content_type = models.ForeignKey(ContentType, default=0)
content_object = fields.GenericForeignKey()
def __str__(self):
return str(self.body)
Views.py
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
class CommentViewSet(viewsets.ModelViewSet):
queryset = Comment.objects.all()
serializer_class = CommentSerializer
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
def index(request):
return render(request, 'index.html', [])
The index template is a "homepage" that loads the latest posts. Under each post, I'm showing the comments. In the JSON those are links, and I found out how to load them trougth the links (so it's working).
Someone told me that instead of doing it this way, I should make it "load" the comments in the backend, and send them together with the posts (the data, not links). He told me to take a look into: http://www.django-rest-framework.org/api-guide/filtering/#overriding-the-initial-queryset
I can't really figure it out.
How do I get the data, insted of links for the ManyToManyField?
To unfold all the related data one level deep you can use depth param:
class PostSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Post
fields = ['title', 'body', 'comments', 'user', 'date']
depth = 1
This will replace post.user and post.comments ids with actual records. depth=2 will also unfold post.comments.user. If you want to selectively pull post.comments only one level deep and not post.user:
class PostSerializer(serializers.HyperlinkedModelSerializer):
comments = CommentSerializer(many=True) #has to be declared higher above
class Meta:
model = Post
fields = ['title', 'body', 'comments', 'user', 'date']
If on top you want to have post.comments.user unfolded, need to either put depth=1 into existing comment serializer or create a new serializer with unfolded users just for this view, similar to examples above.
Also make sure you are using prefetch_related on your queryset or the performance will take a serious hit, like:
Post.objects.all().prefetch_related('comments', 'comments__user')