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
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)
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 am having trouble getting the author of the comment when the form submits
This works fine for my post method, not so much for commenting on the post
Models.py
class Post(models.Model):
title = models.CharField(max_length=100)#title of a post
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE) #if a user is deleted all of their post will be as well
view_count = models.IntegerField(default=0)
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('forum.Post', on_delete=models.CASCADE, related_name='comments')
author = models.ForeignKey(User, on_delete=models.CASCADE)
text = models.TextField(null=True)
created_date = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.text
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
Views.py
class CommentCreate(LoginRequiredMixin,CreateView):
model = Comment
fields = ['text']
def for_valid(self,form):
form.instance.author = self.request.users
return super().form_valid(form)
class PostCreate(LoginRequiredMixin, CreateView):
model = Post
fields = ['title', 'content']
def form_valid(self,form):
form.instance.author = self.request.user
return super().form_valid(form)
class PostDetail(DetailView):
model = Post
def get_context_data(self, **kwargs):
data = super(PostDetail, self).get_context_data(**kwargs)
vc = self.object.view_count
self.object.view_count = F('view_count') + 1
self.object.save()
self.object.view_count = vc + 1
return data
Post_detail template
{% extends "forum/base.html" %}
{% block content %}
<article class="media content-section">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2" href="#">{{ object.author }}</a>
<small class="text-muted">{{ object.date_posted }}</small>
<small class="text-muted">{{ object.view_count }}</small>
{% if object.author == user %}
<div>
<a class="btn btn-secondary btn-sm mt-1 mb-1" href="{% url 'post-update' object.id %}">Update</a>
<a class="btn btn-danger btn-sm mt-1 mb-1" href="{% url 'post-delete' object.id %}">Delete</a>
</div>
{% endif %}
</div>
<h2 class="article-title">{{ object.title }}</h2>
<p class="article-content">{{ object.content }}</p>
<a class="btn btn-default" href="{% url 'add_comment_to_post' pk=post.pk %}">Add comment</a>
{% for comment in post.comments.all %}
<div class="comment">
<div class="date">{{ comment.created_date }}</div>
<strong>{{ comment.author }}</strong>
<p>{{ comment.text|linebreaks }}</p>
</div>
{% empty %}
<p>No comments here yet :(</p>
{% endfor %}
</div>
</article>
{% endblock content %}
The error I am getting is "NOT NULL constraint failed: forum_comment.author_id". Im not sure what is different from my post CreateView
Create a ModelForm for Comment in a forms.py file inside your app:
# your_app/forms.py
from django import forms
from .models import Comment
class CommentModelForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['post', 'text']
widgets = {
'post': forms.HiddenInput(),
'text': forms.Textarea(attrs={'placeholder': 'Write your comment here...'}),
}
After that edit your PostDetail view so you can pass the CommentModelForm with the post instance already set in the form:
class PostDetail(DetailView):
model = Post
def get_context_data(self, **kwargs):
data = super(PostDetail, self).get_context_data(**kwargs)
vc = self.object.view_count
self.object.view_count = F('view_count') + 1
self.object.save()
self.object.view_count = vc + 1
initial_comment_data = {
'post': self.object,
}
data['comment_form'] = CommentModelForm(initial=initial_comment_data)
return data
Add the CommentModelForm to you CommentCreate view:
class CommentCreate(LoginRequiredMixin,CreateView):
model = Comment
form_class = CommentModelForm
def form_valid(self,form):
form.instance.author = self.request.user
return super().form_valid(form)
And finally, the template of your PostDetail should look like this:
# your_app/templates/your_app/post_detail.html
...
<form method="POST" action="{% url 'yourapp:comment-create' %}">
{% csrf_token %}
{{ comment_form }}
<button type="submit">Place a comment</button>
</form>
<div>
{% for comment in post.comments.all %}
<p>{{ comment.text }} - by {{ comment.author }} at {{ comment.created_date }}</p>
{% empty %}
<p>No comments yet. Be the first one!</p>
{% endfor %}
</div>
You have two typos
for_valid should be form_valid
def form_valid(self,form):
form.instance.author = self.request.users
return super().form_valid(form)
and self.request.users should be self.request.user
Try self.request.user instead of self.request.users. And also change for_valid to form_valid. If that doesn't work try something like this. new_comment is the instance that you can save here and return from the function instead of the form.
Inside the form_valid function of CommentCreate class
new_post = form.save(commit=False)
new_post.author = self.request.user
new_post.save()
return new_post
Inside the form_valid function of PostCreate class :
new_comment = form.save(commit=False)
new_comment.author = self.request.user
new_comment.post = super().form_valid(form)
new_comment.save()
return new_comment
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)
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.