I added a like button to my site and it gets highlighted when clicked on it but does not increment the number of likes or shows the count in the admin. I am not understanding what is the mistake I have done. Please help me to solve this. My code is as follows.
models.py
class Post(models.Model):
post = models.TextField()
user = models.ForeignKey(User)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
likes = models.IntegerField(default=0)
def __str__(self):
return self.post
views.py
#login_required
def about(request,pk):
context = {}
template = 'about.html'
return render(request, template, context)
post = get_object_or_404(Post, pk=pk)
Post.objects.get(pk=pk)
post_id = post.pk
liked = False
if request.session.get('has_liked_' + str(post_id), liked):
liked = True
print("liked {}_{}".format(liked, post_id))
context = {'post': post, 'liked': liked}
return render(request, 'imagec/about.html', {'post': post})
#login_required()
def like_post(request):
liked = False
if request.method == 'GET':
post_id = request.GET['post_id']
post = Post.objects.get(id=int(post_id))
if request.session.get('has_liked_'+post_id, liked):
print("unlike")
if post.likes > 0:
likes = post.likes - 1
try:
del request.session['has_liked_'+post_id]
except KeyError:
print("keyerror")
else:
print("like")
request.session['has_liked_'+post_id] = True
likes = post.likes + 1
post.likes = likes
post.save()
return HttpResponse(likes, liked)
urls.py
url(r'like_post/$', imagec_views.like_post, name='like_post'),
about.html
<p>
<strong id="like_count">{{ post.likes }}</strong> people like this category
{% if user.is_authenticated %}
<input type="button" onclick="jQuery(this).toggleClass('active')" data-post_id="{{post.id}}" id="likes" value ='Like'>
{% endif %}
</p>
base.html
<script type="text/javascript" src="{% static 'js/blog-ajax.js' %}"></script>
<script type="text/javascript" src="{% static 'js/jquery-3.js' %}"></script>
<script src="{% static 'js/jquery-3.js' %}"></script>
<script src="{% static 'js/blog-ajax.js' %}"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src = {% static "js/ajax-blog.js" %}> </script>
<script src = {% static "js/jquery-3.js" %}> </script>
ajax-blog.js
$('#likes').click(function(){
var postid;
postid= $(this).attr("data-post_id");
$.get('/imagec/like_post/', {post_id: postid}, function(data){
$('#like_count').html(data);
});
});
Thank You
You can debug it by using console.log in JS side and print in Python side.
ajax-blog.js
$('#likes').click(function(){
var postid;
postid= $(this).attr("data-post_id");
console.log("Post id to be send " + postid);
$.get('/imagec/like_post/', {post_id: postid}, function(data){
console.log("data received: "+data);
$('#like_count').html(data);
});
});
views.py
#login_required
def about(request,pk):
context = {}
template = 'about.html'
return render(request, template, context)
post = get_object_or_404(Post, pk=pk)
Post.objects.get(pk=pk)
post_id = post.pk
liked = False
if request.session.get('has_liked_' + str(post_id), liked):
liked = True
print("liked {}_{}".format(liked, post_id))
context = {'post': post, 'liked': liked}
return render(request, 'imagec/about.html', {'post': post})
#login_required()
def like_post(request):
liked = False
if request.method == 'GET':
post_id = request.GET['post_id']
post = Post.objects.get(id=int(post_id))
if request.session.get('has_liked_'+post_id, liked):
print("unlike")
if post.likes > 0:
likes = post.likes - 1
try:
del request.session['has_liked_'+post_id]
except KeyError:
print("keyerror")
else:
print("like")
request.session['has_liked_'+post_id] = True
likes = post.likes + 1
post.likes = likes
print("updated liked ", post.likes)
post.save()
return HttpResponse(likes, liked)
And check the console prints.
Other than that, there are two main suggestions from my end,
don't use get method to update a value in database
don't use sessions to save a flag that if a user liked a post. That logic might bug you
in the future.
Related
I have a problem. I made likes to the posts on the video from YouTube. It is possible to like a post only from post_detail view and it's works correctly! How to make it possible to like posts in the post_list (in my case it's 'feed')? Please help!
MY CODE:
utils.py
def is_ajax(request):
return request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'
views.py
from .utils import is_ajax
def feed(request):
comments = Comment.objects.all()
posts = Post.objects.all().order_by('-created_at')
return render(request, 'main/feed.html', {'posts': posts,
'comments': comments})
def post_detail(request, slug):
post = get_object_or_404(Post, slug=slug)
comments = Comment.objects.filter(post=post, reply=None).order_by('-created_at')
is_voted = False
if post.votes.filter(id=request.user.id).exists():
is_voted = True
if request.method == 'POST':
comment_form = CommentForm(request.POST or None)
if comment_form.is_valid():
content = request.POST.get('content')
reply_id = request.POST.get('comment_id')
comment_qs = None
if reply_id:
comment_qs = Comment.objects.get(id=reply_id)
comment = Comment.objects.create(
post = post,
author = request.user,
content = content,
reply = comment_qs
)
comment.save()
return redirect(post.get_absolute_url())
else:
for error in list(comment_form.errors.items()):
messages.error(request, error)
else:
comment_form = CommentForm()
return render(request, 'main/post_detail.html', {'post':post,
'comment_form':comment_form,
'comments':comments,
'is_voted':is_voted})
#login_required
def post_vote(request, slug):
post = get_object_or_404(Post, id=request.POST.get('id'), slug=slug)
is_voted = False
if post.votes.filter(id=request.user.id).exists():
post.votes.remove(request.user)
is_voted = False
else:
post.votes.add(request.user)
is_voted = True
context = {
'post': post,
'is_voted': is_voted,
}
if is_ajax(request):
html = render_to_string('main/votes.html', context, request)
return JsonResponse({'form':html})
votes.html
<form action="{% url 'main:post-vote' slug=post.slug %}" method="post">
{% csrf_token %}
{% if is_voted %}
<button type="submit" id="vote" name="post_id" value="{{ post.id }}" btn="btn btn-outline">Unvote</button>
{% else %}
<button type="submit" id="vote" name="post_id" value="{{ post.id }}" btn="btn btn-outline">Vote</button>
{% endif %}
</form>
{{post.votes_count}}
post_detail.html
<div id="vote-section">
{% include 'main/votes.html' %}
</div>
<script>
$(document).ready(function(event){
$(document).on('click','#vote', function(event){
event.preventDefault();
var pk = $(this).attr('value');
$.ajax({
type: 'POST',
url: "{% url 'main:post-vote' slug=post.slug %}",
data: {'id':pk, 'csrfmiddlewaretoken': '{{ csrf_token }}'},
dataType: 'json',
success: function(resoponse){
$('#vote-section').html(resoponse['form'])
console.log($('#vote-section').html(resoponse['form']));
},
error: function(rs, e){
console.log(rs.responseText);
}
})
});
});
</script>
feed.html
...
{% for post in posts %}
<div id="vote-section">
{% include 'main/votes.html' %}
</div>
...
{% endfor %}
<script>
$(document).ready(function(event){
$(document).on('click','#vote', function(event){
event.preventDefault();
var pk = $(this).attr('value');
$.ajax({
type: 'POST',
url: "{% url 'main:post-vote' slug=post.slug %}",
data: {'id':pk, 'csrfmiddlewaretoken': '{{ csrf_token }}'},
dataType: 'json',
success: function(resoponse){
$('#vote-section').html(resoponse['form'])
console.log($('#vote-section').html(resoponse['form']));
},
error: function(rs, e){
console.log(rs.responseText);
}
})
});
});
</script>
models.py
class Post(models.Model):
...
votes = models.ManyToManyField(get_user_model(), related_name='votes', blank=True)
#property
def votes_count(self):
return self.votes.count()
urls.py
urlpatterns = [
path('', views.feed, name='feed'),
path('post_create/', views.post_create, name='post-create'),
path('post/<slug>/', views.post_detail, name='post-detail'),
path('post/<slug>/delete', views.post_delete, name='post-delete'),
path('post/<slug>/update', views.post_update, name='post-update'),
path('post/<slug>/vote', views.post_vote, name='post-vote'),
path('comment/<slug>/delete', views.comment_delete, name='comment-delete'),
path('comment/<slug>/update', views.comment_update, name='comment-update'),
path('comment_reply/<slug>/delete', views.reply_comment_delete, name='comment-reply-delete')
]
I understand that I need to do the same as in post detail. I tried to do this, but i got errors
def post_detail(request, slug):
post = get_object_or_404(Post, slug=slug)
comments = Comment.objects.filter(post=post, reply=None).order_by('-created_at')
is_voted = False
if post.votes.filter(id=request.user.id).exists():
is_voted = True
...
Tried to do:
def feed(request, slug=None):
comments = Comment.objects.all()
posts = Post.objects.all().order_by('-created_at')
v_post = get_object_or_404(Post, slug=slug)
is_voted = False
if v_post.votes.filter(id=request.user.id).exists():
is_voted = True
return render(request, 'main/feed.html', {'posts': posts,
'comments': comments,
'is_voted': is_voted})
got
Page not found (404)
Also tried
def feed(request, slug):
comments = Comment.objects.all()
count_filter = Q(votes=request.user)
vote_case = Count('votes', filter=count_filter, output_field=BooleanField())
posts = Post.objects \
.annotate(is_voted=vote_case) \
.all().order_by('-created_at')
return render(request, 'main/feed.html', {'posts': posts,
'comments': comments})
got
TypeError at /
feed() missing 1 required positional argument: 'slug'
UPD: For the attemp above, I also did something:
set slug to None
def feed(request, slug=None):
And and moved the script to the for-loop (for post in posts)
This gave its results. Now everything works almost as it should, only there is a bug. When you click on the vote button, he votes for for the post below, and vice versa
OK - there's a fair bit to unpack here:
First, generally speaking, it looks you you want Ajax to handle the bulk of the work. The reason (or at least the initial reason) why that isn't working above is in how you replicate forms
For well constructed HTML, only one element per page should have a particular ID
Your form is generated like this
{% for post in posts %}
<div id="vote-section"> #This will repeat for every post
{% include 'main/votes.html' %}
</div>
What this means is that every form will have the same id, vote-section, so
success: function(resoponse){
$('#vote-section').html(resoponse['form'])
won't know where to write.
Similarly, in the forms on list, every button has id="vote"
This isn't an issue on the details page, because there's only one form and one vote/unvote button. On the list page, with multiple forms, it is.
So what to do about it?
First off, add a class to your button.
votes.html
<button type="submit" id="vote" class="vote-button" name="post_id" value="{{ post.id }}" btn="btn btn-outline">Unvote</button>
Next tweak your feed html so it uses IDs correctly. We'll use {{[forloop.counter][1]}} to generate unique IDs.
We'll also move the ajax into the loop, as the slug for each post is different in the url: generation section. We add the new div ID into the selector and the success function to make it unique.
feed.html
{% for post in posts %}
<div id="vote-section{{forloop.counter}}">
{% include 'main/votes.html' %}
</div>
...
<script>
$(document).ready(function(event){
$(document).on('click','#vote-section{{forloop.counter}} .vote-button', function(event){
event.preventDefault();
var pk = $(this).attr('value');
$.ajax({
type: 'POST',
//this needs to be in the for post in posts loop to get post.slug
url: "{% url 'main:post-vote' slug=post.slug %}",
data: {'id':pk, 'csrfmiddlewaretoken': '{{ csrf_token }}'},
dataType: 'json',
success: function(resoponse){
$('#vote-section{{forloop.counter').html(resoponse['form'])
console.log($('#vote-section{{forloop.counter}}').html(resoponse['form']));
},
error: function(rs, e){
console.log(rs.responseText);
}
})
});
});
</script>
{% endfor %}
Now there are probably ways to make this more efficient, but hopefully it should get you over the hump to where it works.
I am looking to modify a form from the user and return that different form in django, however I have tried many different ways, all in views.py, including:
Directly modifying it by doing str(form) += "modification"
returning a new form by newform = str(form) + "modification"
creating a different Post in models, but then I realized that wouldn't work because I only want one post
All the above have generated errors such as SyntaxError: can't assign to function call, TypeError: join() argument must be str or bytes, not 'HttpResponseRedirect', AttributeError: 'str' object has no attribute 'save', and another authority error that said I can't modify a form or something like that.
Here is a snippet from views.py:
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['content']
title = ['title'] #
template_name = 'blog/post_new.html'
success_url = '/'
def form_valid(self, form):
#debugging
tempvar = (str(form).split('required id="id_content">'))[1].split('</textarea></td>')[0] #url
r = requests.get(tempvar)
tree = fromstring(r.content)
title = tree.findtext('.//title')
print(title)
form.instance.author = self.request.user
if "http://" in str(form).lower() or "https://" in str(form).lower():
if tempvar.endswith(' '):
return super().form_valid(form)
elif " http" in tempvar:
return super().form_valid(form)
elif ' ' not in tempvar:
return super().form_valid(form)
else:
return None
models.py:
class Post(models.Model):
content = models.TextField(max_length=1000)
title = models.TextField(max_length=500, default='SOME STRING') #
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
likes= models.IntegerField(default=0)
dislikes= models.IntegerField(default=0)
def __str__(self):
return (self.content[:5], self.title[:5]) #
#property
def number_of_comments(self):
return Comment.objects.filter(post_connected=self).count()
And in home.html, where the post (along with the title and content) is supposed to be shown:
<a
style="color: rgba(255, 255, 255, 0.5) !important;"
href="{% url 'post-detail' post.id %}">
<p class="mb-4">
{{ post.content }}
{{ post.title }} #
</p>
</a>
The original template I'm modifying can be found here.
Thank you so much for your help, I will be very glad to take any advice!!
Ps: I'm using Python 3.7.4
Create a forms.py file inside the app you are talking about, it should look like this:
from django import forms
from . import models
class YourFormName(forms.ModelForm):
class Meta:
model = models.your_model_name
fields = ['field1', 'field2' ,...] # Here you write the fields of your model, this fields will appear on the form where user post data
Then you call that form into your views.py so Django can render it into your template, like this:
def your_view(request, *args, **kwargs):
if request.method == 'POST':
form = forms.YourFormName(request.POST, request.FILES)
if form.is_valid():
instance = form.save(commit=False)
instance.user= request.user
instance.save()
return redirect('template.html') # To redirect if the form is valid
else:
form = forms.YourFormName()
return render(request, "template.html", {'form': form}) # The template if the form is not valid
And the last thing to do is create the template.html:
{% extends 'base.html' %}
{% block content %}
<form action="{% url 'the_url_that_renders_this_template' %}" method='POST' enctype="multipart/form-data">
{% csrf_token %}
{{ form }}
<button type="submit">Submit</button>
</form>
{% endblock content %}
If you want to take the data from DB submitted in that form, you do so with a new function in views.py:
def show_items(request, *args, **kwargs):
data = YourModelName.objects.all()
context = {
"data": data
}
return render(request, "show_items.html", context)
Then in show_items.html:
{% extends 'base.html' %}
{% block content %}
{% for item in data %}
{{item.field1}}
{{item.field2}}
...
{{The items you want to show in that template}}
{% enfor %}
{% endblock content %}
That's what you wanted to do? If not, add a further explanation on what actually you want to do
I am trying to resolve the problem for few days. I want to post the comment without refreshing page. The problem is the div in which comments are is not reloading or disappearing after hitting post button. It depends on how i "catch" it: when I use the id like:
$('#comments').append('<p>' + data.comment.content + '</p><small>' + data.comment.author + '/' + data.comment.date_of_create + '</small>');
it is not reloading and if i use class it disappears. When i refresh the page, comment is being add properly. This is my code below.
views.py
class PostDetailView(FormMixin, DetailView):
model = Post
template_name = 'blog/post_detail.html'
context_object_name = 'post'
form_class = CommentForm
def get_context_data(self, **kwargs):
context = super(PostDetailView, self).get_context_data(**kwargs)
context['form'] = CommentForm(initial={'post': self.object})
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
if request.method == 'POST':
new_comment = Comment.objects.create(
author = self.request.user,
post = self.object,
content = form.cleaned_data['content']
)
return JsonResponse({'comment': model_to_dict(new_comment)}, status=200)
else:
return self.form_invalid(form)
def form_valid(self, form):
form.instance.author = self.request.user
form.instance.post = self.get_object()
form.save()
return super(PostDetailView, self).form_valid(form)
def get_success_url(self):
return reverse('post_detail', kwargs={'slug': self.object.slug})
comments.js
$(document).ready(function(){
$("#commentBtn").click(function(){
var serializedData = $("#commentForm").serialize();
$.ajax({
url: $("commentForm").data('url'),
data: serializedData,
type: 'post',
dataType: 'json',
success: function(data){
console.log(data);
$('#comments').append('<p>' + data.comment.content + '</p><small>' + data.comment.author + '/' + data.comment.date_of_create + '</small>');
$('textarea').val('');
}
})
});
});
post_detail.html
<h3>Dodaj komentarz:</h3>
<form action="" method="POST" id="commentForm" data-url="{% url 'post_detail' slug=post.slug %}">
{% csrf_token %}
{{ form.as_p }}
<button type="button" id="commentBtn">WyĆlij</button>
</form>
<div id="comments">
<h3>Komentarze:</h3>
{% for comment in post.comment_set.all %}
<p>{{ comment.content }}</p>
<small>{{ comment.author }} / {{ comment.date_of_create }}</small>
{% endfor %}
</div>
In my base.html file i placed on the bottom:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="{% static 'js/comment.js' %}"></script>
settings.py
STATIC_URL = '/static/'
STATICFILES_DIR = [
os.path.join(BASE_DIR, 'static')
]
The most annoying thing is I followed some resolutions/tutorials, i do the same and it still not working. Can it be problem with bad configure? Thanks for any help in advance!
console gives object:
https://i.stack.imgur.com/vWp9N.png
Instead of this
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
do this
<script src="https://code.jquery.com/jquery-3.5.1.min.js" ></script>.
I tried to make a blog that allows user posts, store the posts in the db along with the posted date time and the person who posted it.
My problem is that I somehow cannot load the {{form}} to my UI, which makes my form invalid and I just don't know why it doesn't show up the input text box.
I'm not sure if I need a get_post function, but I'll just put it in views.py. (I havnt write the html of that part yet. Just need to see the form first.)
I'm pretty new to Django, can somebody pls help me with this!!! Thanks!
Below are my files.
blog.html file:
{% block posts %}
<div>
<span>New Post: </span>
<form method="post" action="{% url 'posts' %}" enctype="multipart/form-data">
{% csrf_token %}
<table>
{{form}}
<!--not showing in UI-->
</table>
<input id="id_post_button" type="submit" value="Submit" /><br>
</form>
<div>
{% endblock %}
urls.py
urlpatterns = [
path('posts', views.post_action, name='posts'),
path('post/<int:id>', views.get_post, name='post'),
]
Models.py
class PostModel(models.Model):
user_id = models.IntegerField()
post_input_text = models.CharField(max_length=100)
post_profile = models.CharField(max_length=30)
post_date_time = models.DateTimeField(default=timezone.now)
def __str__(self):
return 'id=' + str(self.user_id) + ", post_date_time=" + self.post_date_time + ", post_input_text=" + self.post_input_text + ", post_profile=" + self.post_profile
Views.py:
#login_required
def post_action(request):
print("----post action---")
context = {}
if request.method == "GET":
context['form'] = CreatePost()
context['posts']= PostModel.objects.get(user_id = request.user.id)
return render(request, "socialnetwork/blog.html", context)
form = CreatePost(request.POST, request.FILES)
if not form.is_valid():
print("not valid ~~~~~~~~")
context['form'] = form
context['posts'] = PostModel.objects.get(user_id = request.user.id)
return render(request, "socialnetwork/blog.html", context)
post_input_text = form.cleaned_data.get("post_input_text")
post_date_time = form.cleaned_data.get("post_date_time")
post_profile = form.cleaned_data.get("post_profile")
obj = PostModel.objects.get(
user_id = request.user.id,
)
obj.post_input_text = form.cleaned_data.get("post_input_text")
obj.post_date_time = form.cleaned_data.get("post_date_time")
obj.post_profile = form.cleaned_data.get("post_profile")
obj.save()
form = CreatePost() #refresh the form to original state
context['form'] = form
context['posts'] = obj
return render(request, "socialnetwork/blog.html", context)
def get_post(request, id):
item = get_object_or_404(PostModel, id=id)
print('Picture #{} fetched from db: {} (type={})'.format(id, item.post_input_text, item.post_profile, item.post_date_time))
if not item.post_input_text:
raise Http404
return HttpResponse(item.post_input_text)
forms.py
class CreatePost(forms.Form):
post_input_text = forms.CharField(max_length=100)
post_profile = forms.CharField(max_length=30)
post_date_time = forms.DateTimeField()
Update the template with {{ form.as_table }}, instead of {{form}}
i am new to django when i try to run this project i wasn't getting any input fields in my template current page was only showing the given labels
i don't know where i've gone wrong
can any of you guys help??
these are my models.py file
from django.db import models
# Create your models here.
class Student(models.Model):
sid = models.CharField(max_length=100)
sname = models.CharField(max_length=100)
sclass = models.CharField(max_length=100)
semail = models.EmailField(max_length=100)
srollnumber = models.CharField(max_length=100)
scontact = models.CharField(max_length=100)
saddress = models.CharField(max_length=100)
class Meta:
db_table = "student"
the are my forms.py file
from django import forms
from student.models import Student
class StudentForm(forms.ModelForm):
class Meta:
model = Student
fields = "__all__"
these are my views.py file
from django.shortcuts import render
from student.models import Student
from student.forms import StudentForm
def student_home(request):
return render(request, 'employee/dash.html')
def add_student(request):
if request.method == "POST":
form = StudentForm(request.POST)
if form.is_valid():
try:
form.save()
return render(request, 'employee/dash.html')
except:
pass
else:
form = StudentForm()
return render(request, 'student/addstudent.html')
template
<!DOCTYPE html>
<html lang="en">
<head>
<title>addstudent</title>
</head>
<body>
<a href="/home" >home</a>
<form method="POST" action="/add_student">
{% csrf_token %}
<label>Student ID:</label>
{{ form.sid }}
<label>Name :</label>
{{ form.sname }}
<label>Class :</label>
{{ form.sclass }}
<label>Email ID:</label>
{{ form.semail }}
<label>Roll Number :</label>
{{ form.srollnumber }}
<label>Contact :</label>
{{ form.scontact }}
<label>Address :</label>
{{ form.saddress }}
<button type="submit">Submit</button>
</form>
</body>
</html>
You forget to give the context to the render function:
return render(request, 'student/addstudent.html',context={'form':form})
should work
You have not included any context in your render functions. Your view should be:
def add_student(request):
if request.method == "POST":
form = StudentForm(request.POST)
if form.is_valid():
try:
form.save()
return render(request, 'employee/dash.html', context={'form': form})
except:
pass
else:
form = StudentForm()
return render(request, 'student/addstudent.html', context={'form': form})