I am trying to build a social network in django. In this code I am trying to enter comments to a post through the template box in my template. But the comment is not getting fetched in my database. My code is as below:
My forms.py creates a model form for comments
forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('ctext',)
Models has a seperate comment model which has foreign keys from post model and user model.
models.py
class Post(models.Model):
author = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
imgfile = models.ImageField(upload_to='posts/', blank=True, null=True)
def publish(self):
self.published_date=timezone.now()
self.save()
def __str__(self):
return self.title
class Comment(models.Model):
comment_auth = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
title = models.ForeignKey(Post, on_delete=models.CASCADE)
ctext = models.TextField(blank=True, null=True, max_length=200)
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
def publish(self):
self.published_date=timezone.now()
self.save()
def __str__(self):
return self.ctext
I guess the logic in views is going wrong somewhere as it been shown while debugging
views.py
def post_list(request):
posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
post = get_object_or_404(Post, title=title)
cform = CommentForm()
comments = Comment.objects.all()
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 = post
new_comment.save()
#cform = CommentForm(request.GET)
#data = {}
#Comment.ctext(**data)
#if cform.is_valid():
#comment={}
#comment['ctext'] = request.POST['ctext']
#cform.changed_data['comment_auth'] = request.user
#cform['comment_auth'] = request.user
#cform['comment_auth_id_id'] = request.user
#cform.save()
return render(request, 'users/post_list.html', {'posts': posts, 'comments': comments, 'form': cform})
else:
form = CommentForm()
return render(request, 'users/post_list.html', {'posts': posts, 'comments': comments, 'form': cform})
Template
<div>
<h2>{{ post.title }}</h2>
<p>{{ post.text|linebreaksbr }}</p>
{{image.imgfile.url}}
{% if post.imgfile %}
<img src="{{ post.imgfile.url }}" alt="{{ post.imgfile.url }}">
{% endif %}
<p>By:- {{ post.author }}</p>
<p>published: {{ post.published_date }}</p>
<form method="POST" class="post-form" action="/users/post/list">{% csrf_token %}
{{ form }}
{% for comment in post.comment_set.all %}
<p><b>Comments: </b></p>
<p><b>{{ comment.comment_auth }}: </b>{{ comment.ctext }}</p>
{% endfor %}
<button type="submit" class="save btn btn-default">Comment</button>
</form>
</div>
i am assuming that you have post and comment entries already trough /admin and that you are able to fetch your posts, according to your question here would be the simpliest way to fetch your post-related comments:
{% for post in posts %}
<div>
{{ post.title }}
By - {{ post.author }}
{% for comment in post.comment_set.all %}
<-- is now looking up for all comment entries, where this post is the set foreignkey -->
<p><b>Comments: </b></p>
<p><b>{{ comment.comment_auth }}: </b>{{ comment.ctext }}</p>
{% endfor %}
</div>
{% endfor %}
views.py:
def post_list(request):
posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
context = {'posts': posts}
return render(request, 'users/post_list.html', context)
See django docs
change views.py as follows:
def post_list(request):
posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
comment_form = CommentForm()
comments = Comment.objects.all()
if request.method == "POST":
data = {
'ctext': request.POST['ctext'],
'comment_auth_id': request.user.id,
'title_id': request.POST['title_id']
}
comment_form = CommentForm(data=data)
if comment_form.is_valid():
Comment.objects.create(**data)
return render(request, 'users/post_list.html', {
'posts': posts,
'comments': comments,
'form': comment_form
})
else:
return render(request, 'users/post_list.html', {
'posts': posts,
'comments': comments,
'form': comment_form
})
As I can understand you are trying to post a new comment and your way of doing it is totally wrong and that is the reason why you are not able to show comments. This is the right way to do:
html:
<p>{{ err_msg }}</p>
{% for post in posts %}
<div>
<!-- Other elements -->
<form method="POST" action="/users/post/list">{% csrf_token %}
{{ form }}
<!-- Sending id of post you are commenting on -->
<input type="hidden" name="post_id" value="{{post.id}}">
<button type="submit">Comment</button>
</form>
<p><b>Comments: </b></p>
{% for comment in post.comment_set.all %}
<p><b>{{ comment.comment_auth }}: </b>{{ comment.ctext }}</p>
{% endfor %}
</div>
{% endfor %}
views.py:
def post_list(request):
posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
# I have removed the comments.objects.all
# as you getting them with post.comment_set.all in your template
form = CommentForm()
data = {'posts': posts, 'form': form}
if request.method == "POST":
# get the id of post you are commenting on
cur_post_id = request.POST.get('post_id')
try:
# check if the post exists in the database.
post = Post.objects.get(id=cur_post_id)
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
new_comment = comment_form.save(commit=False)
new_comment.title= post
new_comment.comment_auth = request.user
new_comment.save()
except:
data['err_msg'] = 'Post does not exist!'
return render(request, 'users/post_list.html', data)
Related
I currently have a blog post feature on my site. I want users to be able to create sub posts
(build log ) off of the main posts.
I created this model with a FK to the Post model
class BuildLog(models.Model):
title = models.CharField(max_length=100)
content = RichTextUploadingField(blank=True, null=True)
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey('Post', on_delete=models.CASCADE)
I then added a button on the post page and created a form. Everything is working as expected but when I submit the form I get the error
null value in column "post_id" of relation "blog_buildlog" violates not-null constraint
If I understand the error correctly it is saying I am not passing the PK of the post into the form.
Views
def DetailPostView(request, pk):
model = Post
post = Post.objects.get(pk=pk)
form = CommentForm
comments = Comment.objects.filter(post=post)#.order_by('-create')
if request.method == 'POST':
# A comment was posted
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
new_comment = comment_form.save(commit=False)
new_comment.author = request.user
new_comment.post = post
new_comment.save()
context = {
'post':post, 'form':form, 'comments':comments
}
return render(request, 'blog/post_detail.html', context)
class BuildLogView(LoginRequiredMixin, CreateView):
model = BuildLog
form_class = BuildLogForm
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def save(self, *args, **kwargs):
super(BuildLog,self).save(*args, **kwargs)
Url
path('build-log-form/', BuildLogView.as_view(), name='build-log-form'),
path('post/<int:pk>/', views.DetailPostView, name='post-detail')
How do I go about passing the post PK into the next view?
forms.py
class BuildLogForm(ModelForm):
class Meta:
model = BuildLog
fields = ['title' , 'content']
html
{% extends "blog/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<form method="post" enctype="multipart/form-data" id="PostForm" novalidate>
<!--id="modelForm"-->
{% csrf_token %}
<fieldset class="django-ckeditor-widget">
<legend class="border-bottom mb-4">New Build Log</legend>
{{ form.title | as_crispy_field }}
{{ form.media }}
{{ form.content | as_crispy_field | safe}}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Post</button>
</div>
</form>
</div>
{% endblock content %}
Just incase their is any confusing It goes Post --> BuildLog(subpost)
Django, how to display comments on a blog post in post template only for login user , i made these below mentioned models & views. Now my comments are successfully storing in database with name of user but the problem is its not shown on post . so far only count is displaying to see how many comments posted so far!
def get_comments(self):
return self.comments.all().order_by('-timestamp')
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
timestamp = models.DateTimeField(auto_now_add=True)
commentfield = models.TextField()
post = models.ForeignKey(Blogpost, related_name='comments', on_delete=models.CASCADE)
def __str__(self):
return self.user.username
**My Forms**
from django import forms
from .models import Blogpost, Comment
class CommentForm(forms.ModelForm):
commentfield = forms.CharField(widget=forms.Textarea(attrs={
'class': 'form-control',
'placeholder': 'Type your comment',
'id': 'usercomment',
'rows': '4'
}))
class Meta:
model = Comment
fields = ("commentfield",)
**My Blog Post Views**
def blogpost(request, year, month, day, post):
category_count= get_category_count()
latest = Blogpost.objects.order_by('-publish')[0:4]
post = get_object_or_404(Blogpost, slug= post)
form = CommentForm(request.POST or None)
if request.method == "POST":
if form.is_valid():
form.instance.user = request.user
form.instance.post = post
form.save()
return redirect('.')
context = {'post':post,
'latest': latest,
'category_count': category_count,
'form': form,
}
return render(request, 'blog/blogpost.html', context)
**My Templates**
<div class="post-comments">
<header>
<h3 class="h6">Post Comments<span class="no-of-comments">
({{ post.comments.count }})</span></h3>
</header>
{% for comment in post.get_comments %}
<div class="comment">
<div class="comment-header d-flex justify-content-between">
<div class="user d-flex align-items-center">
<div class="image">
{% if comment.user.author %}
<img src="{{ comment.user.author.profile_picture.url }}" alt="..."
class="img-fluid rounded-circle">
{% else %}
<img src="" alt="..."
class="img-fluid rounded-circle">
{% endif %}
</div>
<div class="title"><strong>{{ comment.user.username }}</strong><span
class="date">{{ comment.timestamp|timesince }} ago</span></div>
</div>
</div>```
In your blogpost view try getting comments manually and pass them in the context, instead of calling a class method inside the template. Template purpose is to display the buisness logic, not write or define it.
def blogpost(request, year, month, day, post):
category_count= get_category_count()
latest = Blogpost.objects.order_by('-publish')[0:4]
post = get_object_or_404(Blogpost, slug=post)
comments = post.comments.all() # here
form = CommentForm(request.POST or None)
if request.method == "POST":
if form.is_valid():
form.instance.user = request.user
form.instance.post = post
form.save()
return redirect('.')
context = {'post':post,
'latest': latest,
'category_count': category_count,
'form': form,
'comments': comments
}
return render(request, 'blog/blogpost.html', context)
then in your template just use:
{% for comment in comments %}
{{ comment }}
{% endfor %}
I'm making this comment system for my blogs.. I already made the model, the ModelForm and the view to display the comments and the blog. I'm just really confused how to save the comments related to a specific blog. I tried to save the comments with a view but I face an IntegrityError. A little help would be appreciated.
Here's my views.py:
#login_required #View to show the blogs and comments related to it
def readblog(request, blog_pk):
Blog = get_object_or_404(blog, pk=blog_pk)
return render(request, 'social_media/readblog.html', {'Blog':Blog,'Form':CommentForm()})
#login_required #view to save the comments
def commentblog(request,blog_pk):
Blog = get_object_or_404(blog,pk=blog_pk)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
Form = form.save(commit=False)
Form.Blog = Blog
Form.save()
return redirect('usersfeed')
Urls.py:
path('commentblog/<int:blog_pk>', views.commentblog, name='commentblog'),
path('readblog/<int:blog_pk>', views.readblog, name='readblog'),
HTML Page to write and save comments (along with the blog):
{{ Blog.title }}
<br>
{{ Blog.text }}
<br>
{% if Blog.image %}
<img src="{{ Blog.image.url }}" alt="">
{% endif %}
<br>
<form action="{% url 'commentblog' Blog.id %}" method="post">
{% csrf_token %}
{{ Form.as_p }}
<button type="submit">Comment!</button>
</form>
{% for i in Blog.BlogComment.all %}
{{ i.comment }}
<b>user:{{ i.user }}</b>
<br>
{% endfor %}
Comment's model:
class BlogComment(models.Model): # --run-syncdb <- (Research about this!)
user = models.ForeignKey(User, on_delete=models.CASCADE)
comment = models.CharField(max_length=250, null=True)
blog = models.ForeignKey(blog, related_name='BlogComment', on_delete=models.CASCADE, blank=True, null=True)
def __str__(self):
return self.comment
Forms.py:
class CommentForm(forms.ModelForm):
class Meta:
model = BlogComment
fields = ['comment']
You need to add the user since it's a not null field in your model:
def commentblog(request,blog_pk):
blog_obj = get_object_or_404(blog,pk=blog_pk)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
form_obj = form.save(commit=False)
form_obj.blog = blog_obj
# add user instance
form_obj.user = request.user
form_obj.save()
return redirect('usersfeed')
I have Comment model in my Django app.
I want to make comment author to be current registered author, not another registered account.
Here is screenshot, I can choose user2 account to post the comment, but currently I'm on user1 account.
Here is my Django Comment model:
class Comment(models.Model):
post = models.ForeignKey(Post,on_delete=models.CASCADE,related_name='comments')
name = models.ForeignKey(User, on_delete=models.CASCADE)
email = models.EmailField()
body = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
active = models.BooleanField(default=False)
class Meta:
ordering = ['created_on']
def __str__(self):
return 'Comment {} by {}'.format(self.body, self.name)
And some code from html form:
<article class="media content-section">
<div class="media-body">
<!-- comments -->
{% if comments.count == 1 %}
<h2>{{ comments.count }} comment</h2>
{% else %}
<h2>{{ comments.count }} comments</h2>
{% endif %}
{% for comment in comments %}
<div class="comments" style="padding: 10px;">
<p class="font-weight-bold">
{{ comment.name }}
<span class=" text-muted font-weight-normal">
{{ comment.created_on }}
</span>
</p>
{{ comment.body | linebreaks }}
</div>
{% endfor %}
{% if new_comment %}
<div class="alert alert-success" role="alert">
Your comment is awaiting moderation
</div>
{% else %}
<h3>Leave a comment</h3>
{% load crispy_forms_tags %}
<form method="post" style="margin-top: 1.3em;">
{{ comment_form | crispy }}
{% csrf_token %}
<button type="submit" class="btn btn-primary btn-lg">Submit</button>
</form>
{% endif %}
</div>
</article>
EDIT
view.py
def post_detail(request, slug):
template_name = 'blog/post_detail.html'
post = get_object_or_404(Post, slug=slug)
comments = post.comments.filter(active=True)
new_comment = None
# Comment posted
if request.method == 'POST':
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
# Create Comment object but don't save to database yet
new_comment = comment_form.save(commit=False)
# Assign the current post to the comment
new_comment.post = post
# Save the comment to the database
new_comment.save()
else:
comment_form = CommentForm()
return render(request, template_name, {'posts': post,
'comments': comments,
'new_comment': new_comment,
'comment_form': comment_form})
You can make the field non-editable, by setting the editable=… parameter [Django-doc] to False. I furthermore advise to use user and not name, since it is a reference to the user object, not the name of the user:
class Comment(models.Model):
post = models.ForeignKey(Post,on_delete=models.CASCADE,related_name='comments')
user = models.ForeignKey(User, editable=False, on_delete=models.CASCADE)
email = models.EmailField()
body = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
active = models.BooleanField(default=False)
class Meta:
ordering = ['created_on']
def __str__(self):
return 'Comment {} by {}'.format(self.body, self.user)
Then in your view, when the form is valid, you set the instance.user of that form to the request.user:
from django.contrib.auth.decorators import login_required
#login_required
def add_comment(request, post_id):
# …
if request.method == 'POST':
form = CommentForm(request.POST, request.FILES)
if form.is_valid():
form.instance.user = request.user
# …
# …
# …
Well, there shouldn't be a choice of users. To identify user just use request.user in your view. I don't know how your view looks like, but you can set name like this:
name = request.user
# or if you're using classes:
name = self.request.user
I have 4 models: User, Blogger, Post and Comment.
Now, in 'post_desc.html', i want to insert a comment box.
{% if user.is_authenticated %}
<form method="post">
{% csrf_token %}
<input type="text" name="comment" style="width: 800px; height: 145px;"></br></br>
<button type="submit">Submit Comment</button>
</form>
{% else %}
<p>Login to comment</p>
{% endif %}
So, this form will only take comment from the user.
But how to store information like 'commented_by' which will the user that is currently logged in and 'commented_on' which will be the post_topic in which he/she is commenting.
How to store these information automatically? in views.py i tried 'request.user' but that didn't worked. Any solutions?
Comment model:
class Comment(models.Model):
commented_by = models.ForeignKey(User, related_name='comments')
commented_on = models.ForeignKey(Post, related_name='comments')
commented_text = models.CharField(max_length=500)
commented_time = models.DateTimeField(auto_now_add=True)
Post model:
class Post(models.Model):
topic = models.CharField(max_length=200)
description = models.TextField()
created_by = models.ForeignKey(User, related_name='posts')
created_on = models.DateTimeField()
I did this in my view.py
def post_desc(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == 'POST':
comment = request.POST['comment']
comments = Comment.objects.create(
commented_text = comment,
commented_on = request.topic,
commented_by = request.user
)
return redirect('post_desc', pk=post.pk)
return render(request, 'post_desc.html', {'post': post})
But it is giving error, "'WSGIRequest' object has no attribute 'post'".
The only thing wrong with your code is that there's no such thing as request.topic. The topic is the Post, which you already have.
comments = Comment.objects.create(
commented_text = comment,
commented_on = post,
commented_by = request.user
)
you have to a create a url that have a pk parameter to know where to post the comment in which post?:
url(r'add/comment/post/(?P<pk>[0-9]+)', views.new_comment, name="comment")
now let's create the view new_comment:
from django.utils import timezone
def new_comment(request, pk):
user = request.user
comment_text = request.POST['your-comment-input-name']
post = Post.objects.get(id=pk)
Comment.objects.create(created_by=user, text=comment_text, post=post, create_at=timezone.localtime(timezone.now()))
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
don't forget to set your form action to action="{% url 'comment' Post.id %}"
I have something like that in my blog project which I develop for improve myself in django python.
my model;
class Comment(models.Model):
post = models.ForeignKey('post.Post', related_name='comments', on_delete=models.CASCADE)
name = models.CharField(max_length=150, verbose_name='Your Name')
comment = models.TextField(verbose_name='Your Comment')
created_date = models.DateTimeField(auto_now_add=True)
my view for post detail page;
def post_detail(request, slug):
post = get_object_or_404(Post, slug=slug)
form = CommentForm(request.POST or None)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.save()
return HttpResponseRedirect('your redirect page')
context = {
'post': post,
'form': form,
}
return render(request, 'post/detail.html', context)
my post detail template page;
<h3>Comments;</h3>
{% if post.comments.count <= 0 %}
<h4>No comment yet!</h4>
{% else %}
{% for comment in post.comments.all %}
<h4>{{ comment.name }} |
<small>{{ comment.created_date|timesince }}</small>
</h4>
<p>{{ comment.comment|linebreaks }}</p>
<hr/>
{% endfor %}
{% endif %}
<h3>Add Comment:</h3>
{% include 'post/comment.html' %}
my comment form template;
{% load crispy_forms_tags %}
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<input class="btn btn-primary" type="submit" value="Add Comment"/>
</form>
Hope this helps.