NOT NULL constraint failed: website_postcomment.post_id

I’ve been trying to figure out how to create a comment reply system where users can enter comments on the main page (index), where posts are being shown. However, I’ve run into a problem where trying to submit a comment displays this error:

NOT NULL constraint failed: website_postcomment.post_id

Through some investigation, I’ve figured out that the error lies with the fact that the form is unsure which post the comment should be related to, which likely means this is a views.py error.

The code I’m using:

views.py:

from django.core import paginator
from django.shortcuts import render, redirect, get_object_or_404
from django.http import HttpResponse
from .models import *
from django.views.generic import ListView, CreateView, UpdateView
from django.views.generic.detail import DetailView
from .forms import *
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth import login
from django.core.paginator import Paginator
import random

def index(request):
    postdata = Post_Data.objects.all()
    profiledata = ProfileData.objects.all()
    likedata = Like.objects.all()
    dislikedata = Dislike.objects.all()
    comment = PostComment.objects.all()

    # Initialize the comment form
    if request.method == 'POST':
        comment_form = PostCommentForm(request.POST)
        if comment_form.is_valid():
            post = comment_form.save(commit=False)
            post.user = request.user
            post.save()
            return redirect('/')
    else:
        comment_form = PostCommentForm()  # Create an empty form

    # Pass the comment form to the template context
    return render(request, 'index.html', {
        'title': 'RoamTrip',
        'postdata': postdata,
        'profiledata': profiledata,
        'likedata': likedata,
        'dislikedata': dislikedata,
        'comment': comment,
        'comment_form': comment_form  # Make sure this is included
    })

forms.py:

class PostCommentForm(forms.ModelForm):
    class Meta:
        model = PostComment
        fields = ['post_comment']

models.py:

from asyncio.windows_events import NULL
from django.contrib import admin
from django.db import models
from django.urls import reverse
from django.contrib.auth.models import User
from django.contrib.auth import get_user_model
from django.conf import settings
from .validators import validate_file_extension

# Create your models here.

class Post_Data (models.Model):
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,  # Set the appropriate behavior here
        null=True
    )
    time_data = models.DateTimeField(help_text='Enter Time of Upload Here:', auto_now_add=True)
    text_data = models.TextField(help_text='Type Here:')
    image_data = models.ImageField(upload_to="website/media/", null=True, blank=True, help_text='Upload Image:')
    video_data = models.FileField(null=True, upload_to="website/media/", blank=True, validators=[validate_file_extension])
    public_data = models.BooleanField(help_text='Public?', null=True)
    approval = models.BooleanField(help_text='Is this post approved?', default=True)

    def get_absolute_url(self):
        return reverse('model-detail-view', args=[str(self.id)])
        
        return self.image_data


class PostComment(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    post = models.ForeignKey(Post_Data, on_delete=models.CASCADE, related_name='post_comment_id')
    post_comment = models.CharField(max_length=500)
    timestamp = models.DateTimeField(auto_now_add=True)
    
    def __str__(self):
        return f"Comment by {self.user} on {self.post}"

index.html:

<form method="POST">
    {% csrf_token %}
    {{ comment_form.as_p }}
    <button type="submit">Submit Comment</button>
</form>

urls.py:

from django.urls import path
from . import views
from django.conf import settings
from django.conf.urls.static import static
from django.contrib.auth import views as auth_views
from .views import PostListView
from .views import create_post
from .views import signup
from .views import post_detail
from .views import toggle_like
from .views import toggle_dislike


urlpatterns = [
    path('', views.index, name='RoamTrip'),
]

urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

your PostComment model post field set null=False.(it is default option.)

it seems be you try save PostComment object without set post attribute.

set null=True or, add post value before save object.

I do want to add the specific post id the user is commenting on as the post value but I’m not sure how to do that.

try:

postdata = Post_Data.objects.all()
    ...
    if request.method == 'POST':
        comment_form = PostCommentForm(request.POST)
        if comment_form.is_valid():
            post = comment_form.save(commit=False)
            post.user = request.user
            **post.post = postdata**
            post.save()
            return redirect('/')

That error doesn’t exist anymore, but there is another one that came up:

Internal Server Error: /
Traceback (most recent call last):
File “C:\Users\baran\AppData\Local\Programs\Python\Python311\Lib\site-packages\django\core\handlers\exception.py”, line 55, in inner
response = get_response(request)
^^^^^^^^^^^^^^^^^^^^^
File “C:\Users\baran\AppData\Local\Programs\Python\Python311\Lib\site-packages\django\core\handlers\base.py”, line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “C:\Users\baran\Documents\Project RoamTrip\RoamTrip\website\views.py”, line 35, in index
post.post = postdata
^^^^^^^^^
File “C:\Users\baran\AppData\Local\Programs\Python\Python311\Lib\site-packages\django\db\models\fields\related_descriptors.py”, line 283, in set
raise ValueError(
ValueError: Cannot assign “<QuerySet [<Post_Data: Post_Data object (1)>, <Post_Data: Post_Data object (4)>, <Post_Data: Post_Data object (5)>, <Post_Data: Post_Data object (13)>, <Post_Data: Post_Data object (17)>, <Post_Data: Post_Data object (41)>, <Post_Data: Post_Data object (42)>, <Post_Data: Post_Data object (43)>, <Post_Data: Post_Data object (44)>, <Post_Data: Post_Data object (45)>, <Post_Data: Post_Data object (46)>, <Post_Data: Post_Data object (47)>, <Post_Data: Post_Data object (48)>, <Post_Data: Post_Data object (49)>]>”: “PostComment.post” must be a “Post_Data” instance.
[05/Oct/2024 09:37:34] “POST / HTTP/1.1” 500 77815

change postdata = Post_Data.objects.all() to Post_Data object.
like:

postdata = Post_Data.objects.get({some query})

This does work if I’m intending to define a specific id, but I’m trying to work out how to automatically get the primary key of the post a user comments on.

Usually you’d supply the ID of the post being replied to in the initial values of the form. See Creating forms from models | Django documentation | Django

So in your view where you have

    comment_form = PostCommentForm()  # Create an empty form

Make it this, with post being a Post_Data object:

    comment_form = PostCommentForm(initial={"post": post})  # Create an empty form

However, that isn’t going to work if you’re listing lots of posts, each with a comment form. Because you need each form to have a different post ID.

I think there are two options:

  1. Render the form in the template more manually so that you can put the post ID into the field in the template as you loop through the posts and forms.
  2. In the view, create one form for each post, setting its initial data as shown above, and pass all of these forms to the template.