'What does "IntegrityError. NOT NULL constraint failed: blog_comment.post_id " stands for?

I've added a Comment model to my application, but when the form is submitted, this error pops:

NOT NULL constraint failed: blog_comment.post_id

Here's models.py:

class Post(models.Model):
    title = models.CharField(max_length=100, null=True, blank=True)
    caption = models.CharField(max_length=100, null=True, blank=True)
    image = models.ImageField(upload_to='post_pics/', null=True, blank=True)
    date_posted = models.DateTimeField(default=timezone.now)
    author = models.ForeignKey(User, on_delete=models.CASCADE)

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('post-detail', kwargs={'pk': self.pk})

class Comment(models.Model):
    post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
    text = models.TextField(null=True)
    date_posted = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return self.text

    def get_absolute_url(self):
        return reverse('post-detail', kwargs={'pk': self.pk})

This is the part for creating a comment in views.py:

class AddCommentView(LoginRequiredMixin, CreateView):
    model = Comment
    fields = ['text']

    def form_valid(self, form):
        form.instance.author = self.request.user
        return super().form_valid(form)

My forms.py:

class CommentForm(forms.ModelForm):

    class Meta:
        model = Comment
        fields = ['text']

The html form:

    <div class="content-section">
        <form method="POST">
            {% csrf_token %}
            <fieldset class="form-group">
                <legend class="border-bottom mb-4">New Comment</legend>
                {{ form|crispy }}
            </fieldset>
            <div class="form-group">
                <button class="btn btn-outline-info" type="submit">Publish</button>
            </div>
        </form>
    </div>


Solution 1:[1]

You must set the value for post because it's a foreign key. In form_valid you should define post = Post.objects.get(id=current_post_id) and you can get current_post_id from the url or receive it as a parameter. Then assign it: form.instance.post = post

Solution 2:[2]

Your problem here is you need to pass the id of the post in the url or as a form field, otherwise django can't retrieve the post.id when you create the comment.

In your url add path('post/<pk>/add-comment', AddCommentView.as_view(), name='add_comment'),

Your view in form_valid

def form_valid(self, form): comment=form.save(commit=False) comment.post=self.kwargs.get(pk) comment.save() super().form_valid(form)
Inside the template in the form tag add action = "{% url 'add_comment' post.pk%}"

Of course this need to be passed inside a template thatdisplay a post.

Solution 3:[3]

You need to edit your forms.py:

from django.forms import ModelForm, widgets

class CommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        fields = ['post', 'text']
        widgets = {
            'post': widgets.HiddenInput
        }

If u will have problems with a redirect, change your def get_absolute_url in comment-model to:

 def get_absolute_url(self):
     self.article_url = Article.objects.get(id=self.article_id).get_absolute_url()
     return self.article_url

Solution 4:[4]

You need to set post_id for your comment. I've just solved this problem myself. I'll try to explain everything as detailed as possible.

First, go to your urls.py and make sure you have a valid path for your AddCommentView. It should look similar to this:

path('post/<int:pk>/addcomment/', AddCommentView.as_view(), name='comment-create'),

The pk argument is the primary key for Post object, which you are trying to comment. Also do not forget to import AddCommentView from your views.

Second, go to your views.py and correct form_valid function in AddCommentView class:

def form_valid(self, form):
    form.instance.post_id = self.kwargs['pk']
    form.instance.author = self.request.user
    return super().form_valid(form)

Your whole class should looke like this:

class AddCommentView(LoginRequiredMixin, CreateView):
    model = Comment
    fields = ['text']  # filds to be displayed in your form. You may also use your custom form_class
    template_name = 'blog/comment_form.html'  # html template with your form

    def form_valid(self, form):
        form.instance.post_id = self.kwargs['pk']  # this is line sets post_id with pk from url
        form.instance.author = self.request.user  # in case you need to display author
        return super().form_valid(form)

    # returns url where you 'll be redirected after the form is correctly filled
    def get_success_url(self):
        pk = self.kwargs["pk"]
        return reverse("post-detail", kwargs={"pk": pk})

Finally, go to your html template, where you have your "add comment" button. In your case, it should be called post_detail.html. Make sure that you pass pk and url name (aka comment-create) correctly. Add this code:

<a class="text-muted" href="{% url 'comment-create' object.pk %}"> Add comment</a>

P.S. Found solution in this tutorial

P.P.S This is my very first answer on stackoverflow. Hope it helped someone.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Juan Antonio BolaƱo
Solution 2 Ben Boyer
Solution 3
Solution 4 Egor Kolobov