How to add a comment liking system in Django - python

I'm attempting to allow a user to like comments of a particular post. However when I attempt to solve this, the expected result doesn't occur.
urls.py:
path('likecomment/<int:pk>/', post_views.likecomment,
name='likecomment'),
views.py:
def likecomment(request, pk):
if request.method == 'POST':
comment = get_object_or_404(Comment, pk=pk)
comment.likes += 1
comment.save()
return redirect('home')
comments.html:
{% for com in comments %}
<br>
<br>
<br>
<b>{{ com.user.username }}</b> &nbsp {{ com.body }}
<br>
<p class="text-muted">{{ com.pub_date_pretty }} &nbsp &nbsp
&nbsp &nbsp &nbsp {{ com.likes }} Likes</p>
<a href="javascript:
{document.getElementById('likecomment').submit()}"> Like </a>
<form id="likecomment" method="post" action="{% url 'likecomment'
com.id %}">
{% csrf_token%}
<input type="hidden">
</form>
<br>
{% endfor %}
Home view:
#login_required(login_url='/login')
def home(request):
posts = Post.objects.all()
return render(request, 'posts/home.html', {'posts': posts})
Render comments:
def comment(request, pk):
form = CommentForm()
comments = Comment.objects.filter(post__pk=pk)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = Comment()
comment.body = form.cleaned_data['body']
comment.user = request.user
comment.post = Post.objects.get(pk=pk)
comment.save()
return redirect('home')
else:
return render(request, 'posts/comments.html', {'error': 'Please submit valid data'})
else:
return render(request, 'posts/comments.html', {'form': form, 'comments': comments})
Like comments view:
def likecomment(request, pk):
if request.method == 'POST':
comment = get_object_or_404(Comment, pk=pk)
comment.likes += 1
comment.save()
return redirect('home')
The like occurs sometimes, however not for the right comment. The functionality isn't working correctly.
Image 1:
Image 2:

You have multiple HTML form elements with the same id (likecomment). Javascript has no way of knowing which one you meant to submit. Try appending a unique identifier, such as {{ forloop.counter }} to the id value, such as:
<form id="likecomment-{{ forloop.counter }}" ...
and
document.getElementById('likecomment-{{ forloop.counter }}').submit()

Related

Django "if request.method == 'POST':" returns False

I'm making Django app and I have an issue, I've never had problem with before. As always in form view, I'm checking if request.method == 'POST' but somehow it returns False,
My code looks like that:
def recipe_create_view(request):
context = {}
form = RecipeForm(request.POST or None)
IngredientFormset = formset_factory(IngredientForm)
formset = IngredientFormset(request.POST or None)
context['form'] = form
context['formset'] = formset
if request.method == 'POST':
if form.is_valid():
if formset.is_valid():
form.save()
print("made a recipe")
for form in formset:
child = form.save(commit=False)
child.recipe = parent
child.save()
print("made a Ingredient")
else:
print("formset is not valid")
else:
print("form is not valid")
else:
print("request method is not correct")
return render(request, 'recipes/create_recipe.html', context)
create_recipe.html file:
<form method="POST">
{% csrf_token %}
<label>recipe</label>
<p>{{form}}</p>
<label>ingredients</label>
{% for form in formset %}
<ul>
<label>name</label>
<li>{{ form.name }}</li>
<label>quantity</label>
<li>{{ form.quantity }}</li>
</ul>
{% endfor %}
<div>
<input type="submit" value="submit" class="button-33" role="button">
</div>
</form>
Where is the problem?
It is necessary to return HttpResponseRedirect after dealing with POST data, the tip is not specific to Django, it's a good web practice in general.
Also, try to maintain both GET and POST request separately, so try below view:
def recipe_create_view(request):
context = {}
form="" # for the error of variable refrenced before assignment.
IngredientFormset=""
formset=""
if request.method == 'POST':
form = RecipeForm(request.POST)
IngredientFormset = formset_factory(IngredientForm)
formset = IngredientFormset(request.POST)
if form.is_valid():
if formset.is_valid():
form.save()
print("made a recipe")
for form in formset:
child = form.save(commit=False)
child.recipe = parent
child.save()
print("made a Ingredient")
return redirect('some_success_path_name')
else:
print("formset is not valid")
else:
print("form is not valid")
else: # GET method
print("request method is GET")
form = RecipeForm()
IngredientFormset = formset_factory(IngredientForm)
formset = IngredientFormset()
context['form'] = form
context['formset'] = formset
return render(request, 'recipes/create_recipe.html', context)
add action in your HTML form and POST in small case.
<form action="/your_backend_url_to_view/" method="post">
{% csrf_token %}
<label>recipe</label>
<p>{{form}}</p>
<label>ingredients</label>
{% for form in formset %}
<ul>
<label>name</label>
<li>{{ form.name }}</li>
<label>quantity</label>
<li>{{ form.quantity }}</li>
</ul>
{% endfor %}
<div>
<input type="submit" value="submit" class="button-33" role="button">
</div>
</form>

Unable to add comments to a post using a ModelForm in Django

views.py
def postdetail(request,pk): # Single Post view.
post = Post.objects.get(id=pk)
comment = post.comments.all()
comment_count = comment.count()
if request.user.is_authenticated:
if request.method == 'POST':
form = CommentForm(data=request.POST)
content = request.POST['cMessage']
if form.is_valid():
print("Yes valid")
form.instance.body = content
new_comment = form.save(commit=False)
print(new_comment)
new_comment.post = post
new_comment.user = request.user
new_comment.save()
return redirect('blog-home')
else:
form = CommentForm()
context = {
'comment_form': CommentForm,
'post' : post,
'comments': comment,
'count': comment_count,
}
return render(request,'post/postdetail.html', context=context)
models.py
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
user = models.ForeignKey(User,on_delete=models.CASCADE, related_name='comments')
body = models.TextField()
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
# active = models.BooleanField(default=True)
class Meta:
ordering = ('created',)
def __str__(self):
return f'Comment by {self.user} on {self.post}'
forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['body']
template
{% if request.user.is_authenticated %}
<!-- respond -->
<div class="respond">
<h3>Leave a Comment</h3>
<!-- form -->
<form name="contactForm" id="contactForm" method="post" action="">
{% csrf_token %}
<fieldset>
<div class="message group">
<label for="cMessage">Message <span class="required">*</span></label>
<textarea name="cMessage" id="cMessage" rows="10" cols="50" ></textarea>
</div>
<button type="submit" class="submit">Submit</button>
</fieldset>
</form> <!-- Form End -->
</div>
{% endif %}
There is no error being displayed neither If I am adding a comment using the shell/through admin panel but if I am trying to add the comment dynamically through the form then the comment is not getting saved.
I have added only the form in the template.
You have defined field body in your CommentForm. It's required in your form, because you didn't include blank=True argument in your model for this field. This means that when you POST request and check if form is valid with form.is_valid(), the form expects an element with a name body in the request. If it's not there, it will not validate and content won't be saved.
Make the following changes:
Change your view to
...
if request.method == 'POST':
form = CommentForm(data=request.POST)
if form.is_valid():
new_comment = form.save(commit=False)
new_comment.post = post
new_comment.user = request.user
new_comment.save()
return redirect('blog-home')
else:
print(form.errors) # or log it to a file, if you have logging set up
form = CommentForm()
...
Change your HTML to this:
...
<form name="contactForm" id="contactForm" method="post" action="">
{% csrf_token %}
<fieldset>
<div class="message group">
<label for="body">Message <span class="required">*</span></label>
<textarea name="body" id="cMessage" rows="10" cols="50" ></textarea>
{{ comment_form.body.errors }}
</div>
<button type="submit" class="submit">Submit</button>
</fieldset>
</form>
...
In views.py
def postdetail(request):
print(Comment.objects.all())
if request.method == 'POST':
form = CommentForm(data=request.POST)
content = request.POST['body']
if form.is_valid():
print("Yes valid")
new_comment = form.save(commit=False)
print(new_comment)
new_comment.post = post
new_comment.user = request.user
new_comment.save()
return redirect('blog-home')
else:
form = CommentForm()
return render(request,'temp/postdetail.html', context=context)
In html file
{% if request.user.is_authenticated %}
<div class="respond">
<h3>Leave a Comment</h3>
<form name="contactForm" id="contactForm" method="post" action="">
{% csrf_token %}
<textarea name="body"cols="30" rows="10"></textarea>
<button type="submit" class="submit">Submit</button>
</form>
</div>
{% endif %}
This worked for me.

redirecting to comment section after login is not working in django

it saves and displays comments on the options.html page if the user is already logged in but when the user logs in after comment section redirects to the login page it displays the value error as this:
NoReverseMatch at /rank/best-trekking-destination-in-nepal/comment/
Reverse for 'comment' with arguments '('',)' not found. 1 pattern(s) tried: ['rank/(?P[^/]+)/comment/$']
Request Method: GET
Request URL: http://127.0.0.1:8000/rank/best-trekking-destination-in-nepal/comment/
Django Version: 2.1.5
Exception Type: NoReverseMatch
Exception Value:
Reverse for 'comment' with arguments '('',)' not found. 1 pattern(s) tried: ['rank/(?P[^/]+)/comment/$']
urls.py
path('<slug>/',views.options,name='options'),
path('<slug>/comment/',views.comment,name='comment'),
views.py
def options(request,slug):
category = Category.objects.get(slug=slug)
category.views += 1
category.save()
options = category.option_set.all().order_by('-votes')
try:
for option in options:
option.has_voted = option.vote_set.filter(voter=request.user).exists()
except:
options = category.option_set.all().order_by('-votes')
form = CommentForm()
return render(request, 'rank/options.html', {'options': options,'form':form,'title': 'options','category':category})
#login_required(redirect_field_name='next',login_url='rank:login')
def comment(request,slug):
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.category = Category.objects.get(slug=slug)
comment.user = request.user
comment.save()
messages.success(request, 'Comment Posted.')
return redirect('rank:options', slug)
else:
form = CommentForm()
return render(request, 'rank/options.html', {'form': form})
def my_login(request):
if request.method == "POST":
form = LoginForm(request.POST)
username = form['username'].value()
password = form['password'].value()
user = authenticate(username=username,password=password)
if user:
login(request,user)
redirect_url = request.GET.get('next','rank:home')
return redirect(redirect_url)
else:
messages.error(request,'Invaid Username or Password')
else:
form = LoginForm()
return render(request,'rank/login.html',{'form':form})
options.html
{% extends "rank/base.html" %}
<title>{% block title %}{{title}}{% endblock title%}</title>
{% load bootstrap4 %}
{% block content %}
<center><br>
<ol type="1">
<center>{% bootstrap_messages %}</center>
{% for option in options %}
<div class="col-lg-6 col-md-6 mb-6">
<div class="card h-100">
<div class="card-body">
<b><li>
<img src="/media/{{option.image}}" width="400" height="300">
<h4>{{option.name}}
</h4>
<h5 class="card-text">{{ option.details}}</h5>
<h5>{{ option.votes }} votes</h5>
{% if option.has_voted %}
<p class="btn btn-success">Voted</p>
{% else %}
<form action="{% url 'rank:vote' option.slug %}" method="post">
{% csrf_token %}
<input type="submit" class="btn btn-success" value="Vote" >
</form>
{% endif %}
</li></b>
</div>
<div class="card-footer">
<small class="text-muted"></small>
</div>
</div>
</div>
{% empty %}
<div class="card w-100">
<div class="card-body">
<h4>Item not available</h4>
</div>
</div>
{% endfor %}
</ol>
<h3>{{ category.comment_set.all|length}} comments</h3>
<hr>
{% for c in category.comment_set.all %}
<div class="col-lg-6 col-md-6 mb-6">
<div class="card-footer text-muted">
<b>{{ c.user.username}} </b>&nbsp {{c.created|timesince}} ago
</div>
<div class="card-body">
<p class="card-text">{{ c.comment}}</p>
</div>
</div>
{% endfor %}
<hr>
<div class="col-lg-6 col-md-6 mb-6">
<form method="post" action="{% url 'rank:comment' category.slug %}">
{% csrf_token %}
{% bootstrap_form form %}
<input type="submit" class="btn btn-success" value="Post Comment">
</form>
</div>
</center>
{% endblock content%}
you comment function in views.py is incomplete. you don't handle GET and other types of request in there:
#login_required(redirect_field_name='next',login_url='rank:login')
def comment(request,slug):
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.category = Category.objects.get(slug=slug)
comment.user = request.user
comment.save()
messages.success(request, 'Comment Posted.')
return redirect('rank:options',slug)
else:
form = CommentForm()
return render(request,'...some_template',{'form':form})
UPDATE: your next error NoReverseMatch is occured because vairable category (and also options) not send from views to the options.html template and therefor it is null in the template (arguments '('',)' not found). you can fix that like this:
#login_required(redirect_field_name='next',login_url='rank:login')
def comment(request,slug):
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.category = Category.objects.get(slug=slug)
comment.user = request.user
comment.save()
messages.success(request, 'Comment Posted.')
return redirect('rank:options', slug)
else:
form = CommentForm()
category = Category.objects.get(slug=slug)
options = category.option_set.all().order_by('-votes')
return render(request, 'rank/options.html', {'options': options,'form':form,'title': 'options','category':category})
#login_required(redirect_field_name='next',login_url='rank:login')
def comment(request,slug):
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.category = Category.objects.get(slug=slug)
comment.user = request.user
comment.save()
messages.success(request, 'Comment Posted.')
return redirect('rank:options',slug)
return redirect('some default view')
You need to add a view to each branch of computation if your POST request view fails or the form is not valid then a view is not returned. I put a line of code you need to finish off.

Django edit comment

So i want to do a edit existing comment, but it gives me this error
ValueError at /episode/Dragon-Ball-Super/edit/11
The view home.views.edit_comment didn't return an HttpResponse object. It returned None instead.
edit_comment
def edit_comment(request, slug, id):
anime = get_object_or_404(Anime, slug=slug)
comment = get_object_or_404(Comment, id=id)
form = CommentForm(request.POST, instance=comment.user)
if request.method == 'POST' or 'NONE':
if form.is_valid():
form.save()
return redirect('anime_title', slug=slug)
else:
form = CommentForm()
context = {'form': form}
return render(request, 'home/edit-comment.html', context)
urls.py
urlpatterns = [
path('', views.index, name='index'),
re_path(r'^episode/(?P<slug>[\w-]+)/$', views.anime_title, name='anime_title'),
re_path(r'^episode/(?P<slug>[\w-]+)/comment/$', views.add_comment, name='add_comment'),
re_path(r'^episode/(?P<slug>[\w-]+)/edit/(?P<id>\d+)/?', views.edit_comment, name='edit_comment'),
]
link under existing comment
{% if comment.user == request.user.userprofile %}
<h6 class="small comment-meta">
Edit
Delete</h6>
{% endif %}
models.py
class Comment(models.Model):
anime = models.ForeignKey(Anime, on_delete=models.CASCADE)
user = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
body = models.TextField()
created = models.DateTimeField(auto_now_add=True)
edit-comment.html
{% extends 'base/base.html' %}
{% block head %}
<title>Edit Comment</title>
{% endblock %}
{% block content %}
<h2>Edit Comment</h2>
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Submit</button>
</form>
{% endblock %}
You are handling only the POST request. GET request will be placed the first time when you load the form.
def edit_comment(request, slug, id):
anime = get_object_or_404(Anime, slug=slug)
comment = get_object_or_404(Comment, id=id)
form = CommentForm()
if request.method == 'POST':
form = CommentForm(request.POST, instance=comment.user)
if form.is_valid():
form.save()
return redirect('anime_title', slug=slug)
return render(request, 'home/edit-comment.html', {'form':form})

Take In Input from User and Display the same input on the same page

Hi i am trying to create a post form in django where the user creates a post and the post is displayed back to them on the same page so far i have been unsuccessful. My articles display on the page but the form to post articles doesnt only the submit button shows.
views.py
def articles(request):
args = {}
args.update(csrf(request))
args ['posts'] = post.objects.filter(user = request.user)
args ['full_name'] = User.objects.get(username = request.user.username)
return render_to_response('articles.html', args)
def article(request, post_id=1):
return render(request, 'article.html',
{'post': post.objects.get(id=post_id) })
def create(request):
if request.POST:
form = PostForm(request.POST, request.FILES)
if form.is_valid():
a = form.save(commit=False)
a.user = User.objects.get(username = request.user.username)
a.save()
messages.add_message(request, messages.SUCCESS, "You Article was added")
return HttpResponseRedirect('/posts/all')
else:
form = PostForm()
args = {}
args.update(csrf(request))
args['form'] = form
return render_to_response('articles.html', args)
articles.html
<form action="/posts/create/" method="post" enctype="multipart/form-data">{% csrf_token %}
<ul>
{{form.as_ul}}
</ul>
<input type="submit" name="submit" value="Create Article">
</form>
{% if posts.count > 0 %}
{% for post in posts %}
<div>
<h2>{{full_name}}</h2>
<p>{{ post.body|lower|truncatewords:50 }}</p>
<p>{{post.likes}} people liked this article</a></p>
</div>
{% endfor %}
{% else %}
<p>None to show!</p>
{% endif %}
{% endblock %}
urls.py
from django.conf.urls import patterns, include, url
urlpatterns = patterns('',
url(r'^create/$', 'posts.views.articles'),
url(r'^get/(?P<post_id>\d+)/$', 'posts.views.article'),
url(r'^create/$', 'posts.views.create'),
url(r'^like/(?P<post_id>\d+)/$', 'posts.views.like_article'),
url(r'^article/(?P<post_id>\d+)/$', 'posts.views.add_comment'),
)
First off you aren't passing the form to the template in articles(). You need to have something along the lines of:
args['form'] = YourForm()
In your first view function. Also, in your create view you do this:
a.user = User.objects.get(username = request.user.username)
Which does an unnecessary database lookup of the user again, you can just do this to be clearer and faster:
a.user = request.user

Categories

Resources