How To Solve Django ValueError: :Comment.post" must be a "Software" instance

I'm getting an error over and over again when I'm displaying comments for a program. Please what is the problem I made here? I made many attempts using get , filter but the error remains the same. Other errors may appear when changing the view of comments. Please help.

    def download(request,slug):   
        app = get_object_or_404 (Software,slug = slug)
        comments= app.comments.filter(active=True).order_by("-created")
        try :
            app  = Software.objects.filter(slug = slug)
        except :
            raise Http404
        new_comment = None
        if request.method == "POST":
        comment_form = CommentForm(data=request.POST)
             if comment_form.is_valid():
                new_comment = comment_form.save(commit=False)
                new_comment.post= app <<<--------------------------->>> here my error
                new_comment.save()
        else:
             comment_form = CommentForm()     
        context ={
            'app' :app,
            'comments': comments,
            'new_comment': new_comment,
            'comment_form': comment_form,       
        }
        return render( request,'download.html',context)

that's my models:

class Software (models.Model): 
   class Meta :
   verbose_name_plural = 'software'
   ordering = ['created_at']
   category = models.ForeignKey(Categorie,on_delete=models.CASCADE,null=True, related_name="mac")
   category = models.CharField(max_length=150 , null=True,blank=True)
   slug = models.SlugField(blank=True,allow_unicode=True,editable=True)
   title = models.CharField(max_length=50 , null=True)
   requirements = models.CharField(max_length=100 , null=True)
   version = models.CharField(max_length=100 , null=True)
   picture = models.ImageField(upload_to='img_pro' , null=True)
   size = models.CharField(max_length=20 , null=True)
   created_at = models.DateField(auto_now_add=True)
   auther = models.CharField(max_length=100 , null=True)
   download = models.URLField(null=True)



   def __str__(self):
      return self.slug

   def get_absolute_url(self):
      return reverse('download', args=(self.slug,))  


class Comment(models.Model):
   post = models.ForeignKey(Software , on_delete=models.CASCADE ,null=True, related_name='comments')
   name = models.CharField(max_length=80 , null=True)
   email = models.EmailField(max_length=200, blank=True , null=True)
   body = models.TextField( null=True)
   created = models.DateTimeField(auto_now_add=True , null=True)
   active = models.BooleanField(default=False , null=True)
   parent = models.ForeignKey('self', null=True, blank=True, related_name='replies',on_delete=models.CASCADE)

   class Meta:
      ordering = ('created',)

   def __str__(self):
      return 'Comment by {}'.format(self.post)

Ok, obj.objects.filter() always returns a queryset. So in your case variable app is a queryset and not an instance of model Software.

To ensure that variable app only contains one single instance of Software you would want to use obj.objects.get() which results in either a DoesNotExistError, a MultipleObjectsReturnedError or a single hit for your wanted model instance of Software. Here you go with lookups with ...objects.get().

To replace your try-except-block entirely with the convenient function get_object_or_404() do like this:

def download(request,slug):   
    app = get_object_or_404(Software, slug=slug)
    if request.method == "POST":
        comment_form = CommentForm(data=request.POST)
        if comment_form.is_valid():
            new_comment = comment_form.save(commit=False)
            new_comment.comments = app <<<--------------------------->>> here my error
            new_comment.save()
    else:
        comment_form = CommentForm()     
    context ={
        'app' :app,
        'comments': app.comments,
        'new_comment': new_comment,
        'comment_form': comment_form,       
        }
    return render( request,'download.html',context)

The other issue could be, that you refer to your posts with comment.post = but within your Comment model you told via related_name that you want to refer to your posts via comments.

You need to use .get() instead of .filter() Query for getting object like this...

app  = Software.objects.get(slug = slug) <--- this need to be correct

new_comment = comment_form.save(commit=False)
new_comment.comments = app 
new_comment.save()
Back to Top