Home > Software design >  Cannot query "Product": Must be "Comment" instance
Cannot query "Product": Must be "Comment" instance

Time:08-09

I'm trying to add a commenting and replying system to my products model but I can't add replies to comment. This is being done in the same page where the product details are being shown to the user.

Edit: I'm getting a Cannot assign "<Product: Test Product>": "Reply.comment" must be a "Comment" instance. error at new_reply = Reply(content=content, author=self.request.user, comment=self.get_object())

views.py:

class ProductFeedbackView(DetailView):
    model = Product
    template_name = 'store/product_feedback.html'

    def get_context_data(self , **kwargs):
        data = super().get_context_data(**kwargs)

        connected_comments = Comment.objects.filter(product=self.get_object())
        number_of_comments = connected_comments.count()
        data['comments'] = connected_comments
        data['no_of_comments'] = number_of_comments
        data['comment_form'] = CommentForm()

        connected_replies = Reply.objects.filter(comment=self.get_object())
        number_of_replies = connected_replies.count()
        data['replies'] = connected_replies
        data['no_of_replies'] = number_of_replies
        data['reply_form'] = ReplyForm()

        return data

    def post(self , request , *args , **kwargs):
        if self.request.method == 'POST':
            reply_form = ReplyForm(self.request.POST)
            if reply_form.is_valid():
                content = reply_form.cleaned_data['content']
            new_reply = Reply(content=content, author=self.request.user, comment=self.get_object())
            new_reply.save()
            return redirect(self.request.path_info)

        if self.request.method == 'POST':
            comment_form = CommentForm(self.request.POST)
            if comment_form.is_valid():
                content = comment_form.cleaned_data['content']
            new_comment = Comment(content=content, author=self.request.user, product=self.get_object())
            new_comment.save()
            return redirect(self.request.path_info)

models.py:

class Product(models.Model):
    author = models.ForeignKey(User, default=None, on_delete=models.CASCADE)
    title = models.CharField(max_length=120, unique=True)
    description = models.CharField(max_length=300, blank=True, null=True)


class Comment(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE, blank=True, null=True, related_name='comments')
    author = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True,)

    content = models.CharField(max_length=200, null=True, blank=False)


class Reply(models.Model):
    comment = models.ForeignKey(Comment, on_delete=models.CASCADE)
    author = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True,)

    content = models.TextField(null=True, blank=False)

CodePudding user response:

As the error message suggests, you're trying to assign a Product instance to a field that expects a Comment instance.

This is the line where you try to do this:

connected_replies = Reply.objects.filter(comment=self.get_object())

self.get_object() returns a Product instance as you defined model = Product on your View.

To get the replies connected to your product, you will need to loop over all comments and per comment all its replies as you defined these relations as foreignkeys.

For example:

for comment in connected_comments:
    comment_replies = Reply.objects.filter(comment=comment)

CodePudding user response:

@Vincent answer is ok, the error is from wrong model passed to filter of Replay model.

But for remedy to make it easier in template for showing comments and replies to those comments i suggest delete from context

data['replies'] 
data['no_of_replies']

and in template where you loop through comments (just example):

{% for comment in comments %}
   <h1>{{comment}}</h1>
   {% for reply in comment.reply_set.all %}
       <p>{{ reply }} </p>
   {% endfor %}
{% endfor %}

use reverse relationship with reply_set.

Oh, and for optimization add prefetch_related to your query:

Comment.objects.filter(product=self.get_object()).prefetch_related('reply_set')
  • Related