I'm working on a Django blog and I'm stuck here... I want to post comment and when I do that it just refresh the page and nothing happens.
I have no idea where I'm making a mistake, please see my code below:
this is views.py
def post_detail(request, slug):
post = get_object_or_404(Post, slug=slug)
related_posts = post.tags.similar_objects()[:3]
comments = post.comments.filter(active=True)
new_comment = None
if request.method == 'POST':
if request.user.is_authenticated:
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
new_comment = comment_form.save(commit=False)
new_comment.post = post
new_comment.author = request.user
new_comment.save()
else:
messages.warning(request, 'You need to be logged in to add a comment.')
else:
if request.user.is_authenticated:
comment_form = CommentForm(initial={'author': request.user})
else:
comment_form = CommentForm()
context = {'post': post, 'related_posts': related_posts, 'comments': comments, 'new_comment': new_comment, 'comment_form': comment_form}
return render(request, 'post_detail.html', context)
this is comment part post_detail.html
{% if user.is_authenticated %}
<!--comment form-->
<div class="comment-form">
<h3 class="mb-30">Leave a Reply</h3>
<form class="form-contact comment_form" action="{% url 'post_detail' post.slug %}" method="post">
{% csrf_token %}
<div class="row">
<div class="col-12">
<div class="form-group">
{{ comment_form | crispy }}
</div>
</div>
</div>
<div class="form-group">
<button type="submit" class="button button-contactForm">Post Comment</button>
</div>
</form>
</div>
{% else %}
<div class="alert alert-danger" role="alert">
Please log in to post a comment.
</div>
{% endif %}
this is models.py
class Post(models.Model):
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Created at")
updated_at = models.DateTimeField(auto_now=True, verbose_name="Updated at")
published_at = models.DateTimeField(null=True, blank=True, editable=False, verbose_name="Published at")
post_title = models.CharField(max_length=200, verbose_name="Title")
slug = models.SlugField(max_length=200, unique=True)
author = models.ForeignKey('auth.User', verbose_name="Author", on_delete=models.CASCADE)
category = models.ForeignKey(Category, verbose_name="Category", on_delete=models.CASCADE)
tags = TaggableManager()
body = QuillField()
image = StdImageField(upload_to='featured_image/%Y/%m/%d/', variations={'standard':(1170,820),'banner':(1170,530),'single_post':(1170,556),'thumbnail':(500,500)})
image_credit = models.CharField(max_length=200, verbose_name="Image credit:")
status = models.IntegerField(choices=STATUS, default=0)
def get_absolute_url(self):
return reverse('post_detail', args=[self.slug])
def get_readtime(self):
result = readtime.of_text(self.body)
return result.text
class Meta:
verbose_name = "Post"
verbose_name_plural = "Posts"
ordering = ['-created_at']
def publish(self):
self.is_published = True
self.published_at = timezone.now()
self.save()
def __str__(self):
return self.post_title
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
body = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
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.author.username)
this is forms.py
class CommentForm(forms.ModelForm):
author = forms.CharField(widget=forms.HiddenInput)
class Meta:
model = Comment
fields = ('body', 'user')
Please not that I can post comment over admin page (http://127.0.0.1:8000/admin), but on the front-end doesn't work for some reason.
Any ideas?
You need to redirect() after dealing with POST data, the tip is not specific to Django, its a good web practice in general so:
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
def post_detail(request, slug):
post = get_object_or_404(Post, slug=slug)
related_posts = post.tags.similar_objects()[:3]
comments = post.comments.filter(active=True)
new_comment = None
if request.method == 'POST':
if request.user.is_authenticated:
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
new_comment = comment_form.save(commit=False)
new_comment.post = post
new_comment.author = request.user
new_comment.save()
return redirect(post.get_absolute_url()) # redirect here
else:
messages.warning(request, 'You need to be logged in to add a comment.')
else:
if request.user.is_authenticated:
comment_form = CommentForm(initial={'author': request.user})
else:
comment_form = CommentForm()
context = {'post': post, 'related_posts': related_posts, 'comments': comments, 'new_comment': new_comment, 'comment_form': comment_form}
return render(request, 'post_detail.html', context)
Related
I want to make reply of comment function. so I added parent option in Answer model. answer_create() working well.
but, when i submit reply of comment, url is different with url i setting.
i think i should be http://127.0.0.1:8000/debateboard/305/
but return http://127.0.0.1:8000/debateboard/answer/reply/31/
help me plz
models.py
class Question(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='author_question')
subject = models.CharField(max_length=200)
content = models.TextField()
create_date = models.DateTimeField()
modify_date = models.DateTimeField(null=True, blank=True)
voter = models.ManyToManyField(User, related_name='voter_question')
def __str__(self):
return self.subject
class Answer(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='author_answer')
question = models.ForeignKey(Question, on_delete=models.CASCADE)
content = models.TextField()
create_date = models.DateTimeField()
modify_date = models.DateTimeField(null=True, blank=True)
voter = models.ManyToManyField(User, related_name='voter_answer')
######### i added this parent to make reply of comment
parent = models.ForeignKey('self', on_delete=models.CASCADE, blank=True, null=True, related_name='+')
def __str__(self):
return self.content
#property
def children(self):
return Answer.objects.filter(parent=self).order_by('-create_date').all()
#property
def if_parent(self):
if self.parent is None:
return True
return False
urls.py
urlpatterns = [
path('', base_views.index, name = 'index'),
path('<int:question_id>/', base_views.detail, name = 'detail'),
path('answer/create/<int:question_id>/', answer_views.answer_create, name = 'answer_create'),
path('answer/reply/<int:answer_id>/', answer_views.reply_comment_create , name='reply_comment_create'),
view
def detail(request, question_id):
# question = Question.objects.get(id = question_id)
question = get_object_or_404(Question, pk = question_id)
context = {'question' : question}
return render(request, 'debateboard/detail.html', context)
#login_required(login_url='common:login')
def reply_comment_create(request, answer_id):
answer = get_object_or_404(Answer, pk=answer_id)
# question = get_object_or_404(Question, pk = answer.question.id)
if request.method == "POST":
form = AnswerForm(request.POST)
if form.is_valid():
sub_answer = form.save(commit=False)
sub_answer.author = request.user
sub_answer.create_date = timezone.now()
sub_answer.parent = answer.id
sub_answer.save()
# return redirect('{}#answer_{}'.format(resolve_url('debateboard:detail', question_id = answer.question.id), answer.id))
return redirect('debateboard:detail', question_id = answer.question.id)
else:
return HttpResponseNotAllowed('only post is possible')
context = {'question' : answer.question, 'form': form}
return render(request, 'debateboard/detail.html', context)
#login_required(login_url = 'common:login')
def answer_create(request, question_id):
question = get_object_or_404(Question, pk = question_id)
# question.answer_set.create(content=request.POST.get('content'), create_date = timezone.now())
if request.method == 'POST':
form = AnswerForm(request.POST)
if form.is_valid():
answer = form.save(commit=False)
answer.author = request.user
answer.create_date = timezone.now()
answer.question = question
answer.save()
# return redirect('debateboard:detail', question_id = question.id)
return redirect('{}#answer_{}'.format(resolve_url('debateboard:detail', question_id = question.id), answer.id))
else:
return HttpResponseNotAllowed('Only POST is possible.')
context = {'question': question, 'form': form}
return render(request, 'debateboard/detail.html', context)
html
<!-- for reply of comment -->
<form action="{% url 'debateboard:reply_comment_create' answer.id %}" method="POST">
{% csrf_token %}
<div class="form-group">
<textarea name="reply_comment_{{ answer.id }}" id="reply_comment_{{ answer.id }}" cols="20" rows="5"></textarea>
<button type="submit">dd</button>
</div>
</form>
<!-- for comment-->
<form action="{% url 'debateboard:answer_create' question.id %}" method="post">
{% csrf_token %}
<div class="form-group">
<textarea {% if not user.is_authenticated %}disabled placeholder="로그인후 이용가능" {% endif %} name="content" id="content" cols="30" rows="15"></textarea>
</div>
<input {% if not user.is_authenticated %}disabled placeholder="로그인후 이용가능" {% endif %} type="submit" class="btn btn-primary" value="답변등록">
</form>
The address is structured this way because you add an object of class Answer. Anyway, look at how your url is constructed.
path('answer/reply/<int:answer_id>/', answer_views.reply_comment_create , name='reply_comment_create'),
If you want, check the SlugField which can be useful for create custom url.
I am following the guide to create comment given by Django central, https://djangocentral.com/creating-comments-system-with-django/ and it is working. However I am using the {{ form.as_p }} Which will give 3 fields, as the form say, with name, email and the body. But i wanted to have predefined name, which would be your username you are logged inn with and the email attached to that account. How would i go ahead to create that?
forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['name', 'email', 'body']
models.py
class Comment(models.Model):
post = models.ForeignKey(Post, related_name='comments', on_delete=models.CASCADE)
name = models.CharField(max_length=255)
email = models.EmailField()
body = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-date_added']
def __str__(self):
return self.name
views.py
def post_detail(request, category_slug, slug, status=Post.ACTIVE):
post = get_object_or_404(Post, slug=slug)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.save()
return redirect('post_detail', category_slug=category_slug, slug=slug)
else:
form = CommentForm()
return render(request, 'blog/post_detail.html', {'post': post, 'form': form})
in the html template
{% if user.is_authenticated %}
<h2 class="subtitle is-4">Comments</h2>
<form method="post" class="mb-6">
{% csrf_token %}
{{ form.as_p }}
<div class="field">
<div class="control">
<button class="button is-success">Submit comment</button>
</div>
</div>
</form>
{% endif %}
If you want to pre-set the username and email fields, you can use the initial form parameters like this:
views.py
def post_detail(request, category_slug, slug, status=Post.ACTIVE):
post = get_object_or_404(Post, slug=slug)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.save()
return redirect('post_detail', category_slug=category_slug, slug=slug)
else:
user = request.user
form = CommentForm(initial={"name": user.username, "email": user.email})
return render(request, 'blog/post_detail.html', {'post': post, 'form': form})
forms.py
class CommentForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['name'].disabled = True
self.fields['email'].disabled = True
# OR set readonly widget attribute.
# self.fields['name'].widget.attrs['readonly'] = True
# self.fields['email'].widget.attrs['readonly'] = True
class Meta:
model = Comment
fields = ['name', 'email', 'body']
I have multiple comments on my homepage, how do i limit the comment to be displayed on a post. Like, i have 10 comments and i want to display only 2 comments.
class Image(models.Model):
imageuploader_profile = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE, null=True, blank=True)
first_upload_image = models.FileField(upload_to ='picmate',null=True, blank=True)
second_upload_image = models.FileField(upload_to ='picmate',null=True, blank=True)
image_caption = models.CharField(max_length=700)
date = models.DateTimeField(auto_now_add=True, null= True)
class Meta:
verbose_name = 'Image'
verbose_name_plural = 'Images'
ordering = ['-date']
def __str__(self):
return self.image_caption
class Comments (models.Model):
comment_post = models.TextField()
author = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE, null=True, blank=True)
commented_image = models.ForeignKey('Image', on_delete=models.CASCADE, related_name='comments')
date = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name = 'Comment'
verbose_name_plural = 'Comments'
ordering = ['-date']
def __str__(self):
return self.author.__str__()
def home(request):
all_images = Image.objects.filter(imageuploader_profile=request.user.id)
users = User.objects.all()
next = request.GET.get('next')
if next: return redirect(next)
context = {
'all_images': all_images,
'users': users,
}
return render(request,'home.html', context,)
#login_required
def comments(request, id):
post = get_object_or_404(Image,id=id)
# current_user = request.user
print(post)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.author = request.user
comment.commented_image = post
comment.save()
return redirect('/')
else:
form = CommentForm()
all_comments = Comments.objects.filter(
author=request.user.id,
commented_image=post,
)
{% for comment in post.comments.all %}
<div class="mb-1" style="padding-bottom:-10px;position:relative;top:-10px">
<div class="truncate card-meta dark-grey-text" style="">
<a href="#" class="username dark-grey-text text-lowercase">
{{ comment.author }}</a> {{ comment.comment_post }}
</div>
</div>
{% endfor %}
You can use the django template tag slice
{% for comment in post.comments.all|slice:":2" %}
{{ comment.author }}
{% endfor %}
I'm building a post-comment model in one view, one page, something like facebook. I have two forms in my home.html and view.py: new post and new comment. In each post container, there is a new comment form.
I have a problem because I don't know how to relate comment to post - specifically how to pass post.id to my comment form.
Is it possible to pass my {{ post.id }} to my {{newCommentForm.field }}? That each comment has a default value of post's id?
My home.html:
{% for post in posts %}
<div class="container">
<a class="user" href="#">{{ post.author }}</a>, {{ post.date_posted }}
<img src="{{ post.author.profile.image.url }}" alt="{{ post.author }}"style="width:100%;">
<p>{{ post.content }}</p>
<form METHOD="POST" class="new_post">
{% csrf_token %}
{{ newCommentForm.content }}
{{ newCommentForm.post }}
<button type="submit" name="newCommentSubmit">Add</button>
</form>
</div>
{% endfor %}
models.py
class Post(models.Model):
content = models.TextField(max_length=1000)
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.content
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
author = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.TextField(max_length=500)
date = models.DateTimeField(default=timezone.now)
def add_coment(self):
self.date = timezone.now()
self.save()
def __str__(self):
return self.content
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.jpg', upload_to='profile_pics')
def __str__(self):
return f'{self.user.username}'
views.py
#login_required()
def home(request):
newPostForm = newPost()
newCommentForm = newComment()
if request.method == 'POST':
if 'newPostSubmit' in request.POST:
newPostForm = newPost(request.POST, prefix='newpost')
if newPostForm.is_valid():
instance = newPostForm.save(commit=False)
instance.author = request.user
instance.date_posted = timezone.now()
instance.save()
newCommentForm = newComment(prefix='newcomment')
elif 'newCommentSubmit' in request.POST:
newCommentForm = newComment(request.POST, prefix='newcomment')
if newCommentForm.is_valid():
instance = newCommentForm.save(commit=False)
instance.author = request.user
instance.date_posted = timezone.now()
instance.save()
newPostForm = newPost(prefix='newpost')
else:
newPostForm = newPost(prefix='newpost')
newCommentForm = newComment(prefix='newcomment')
context = {
'newPostForm': newPostForm,
'newCommentForm': newCommentForm,
'posts': Post.objects.all().order_by('-date_posted'),
'comments': Comment.objects.all()
}
return render(request, 'blog/home.html', context)
def about(request):
return render(request, 'blog/about.html')
My model is working now, comments are added, but I need to choose my post (post.id) manually from the default dropdown field witch all posts.
Add your post to comment like this:
if newCommentForm.is_valid():
instance = newCommentForm.save(commit=False)
instance.author = request.user
instance,=.post = request.post
instance.date_posted = timezone.now()
instance.save()
And send post in request!
I have a problem when I was creating the comment form with django. After I wrote my view.py, models.py and html, I got an ValueError that said:
Cannot assign "<class 'blog.models.post'>": "Comment.post" must be a "post" instance".
Below are my codes.
HTML
{% block content %}
<h1>Add New Comment:</h1>
<form method='POST' action=''>
{% csrf_token %}
{{ form.as_p }}
<button type='submit'>Submit</button>
</form>
{% endblock %}
views.py
def add_comment(request, slug):
po = get_object_or_404(post, slug=slug)
if request.method == 'POST':
form = CommentForm(request.POST or None)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.save()
return redirect('blog:post', slug=post.slug)
else:
form = CommentForm()
return render(request, 'blog/post/add_comment.html', {'form': form})
models.py
class Comment(models.Model):
post = models.ForeignKey(post, related_name='comments', on_delete=models.CASCADE)
user = models.CharField(max_length=250)
email = models.EmailField()
body = models.TextField()
created = models.DateTimeField(auto_now_add=True)
approved = models.BooleanField(default=False)
def approved(self):
self.approved = True
self.save()
def __str__(self):
return self.user
The post you fetched from the database is po:
po = get_object_or_404(post, slug=slug)
Therefore you should set form.post = po:
def add_comment(request, slug):
po = get_object_or_404(post, slug=slug)
if request.method == 'POST':
form = CommentForm(request.POST or None)
if form.is_valid():
comment = form.save(commit=False)
comment.post = po
comment.save()
Note that normally in Django you would use Post for your model and post for the instance you fetch from the database.
Try it:
class Comment(models.Model):
post = models.ForeignKey(Post, related_name='comments', on_delete=models.CASCADE)