How to fix no reverse match error in Ajax - django

I am trying to use Ajax to submit a like button without getting the page refreshing, I have been getting several errors previously but now I am getting this error:
django.urls.exceptions.NoReverseMatch: Reverse for 'like_post' with arguments '('',)' not found. 1 pattern(s) tried: ['score/like/(?P<pk>[0-9]+)$']
I am not sure what is the reason. Need help to identify the error.
Here is the view:
class PostDetailView(DetailView):
model = Post
template_name = "post_detail.html"
def get_context_data(self, *args, **kwargs):
context = super(PostDetailView, self).get_context_data()
stuff = get_object_or_404(Post, id=self.kwargs['pk'])
total_likes = stuff.total_likes()
liked = False
if stuff.likes.filter(id=self.request.user.id).exists():
liked = True
context["total_likes"] = total_likes
context["liked"] = liked
return context
def LikeView(request, pk):
# post = get_object_or_404(Post, id=request.POST.get('post_id'))
post = get_object_or_404(Post, id=request.POST.get('id'))
like = False
if post.likes.filter(id=request.user.id).exists():
post.likes.remove(request.user)
like = False
else:
post.likes.add(request.user)
like = True
context["total_likes"] = total_likes
context["liked"] = liked
if request.is_ajax:
html = render_to_string('like_section.html', context, request=request)
return JsonResponse({'form': html})
Here is the url.py updated:
urlpatterns = [
path('user/<str:username>', UserPostListView.as_view(), name='user-posts'),
path('', PostListView.as_view(), name='score'),
path('who_we_Are/', who_we_are, name='who_we_are'),
path('<int:pk>/', PostDetailView.as_view(), name='post-detail'),
path('like/<int:pk>', LikeView, name='like_post'),
path('new/', PostCreateView.as_view(), name='post-create'),
path('<int:pk>/update/', PostUpdateView.as_view(), name='post-update'),
path('<int:pk>/delete/', PostDeleteView.as_view(), name='post-delete')
]
Here is the template:
<form class="mt-0" action="{% url 'score:like_post' post.pk %}" method='POST'>
{% csrf_token %}
<strong> Likes: {{total_likes}} </strong>
{% if user.is_authenticated %}
{% if liked %}
<button id='like' type='submit' name='post_id' class= "btn btn-danger btn-sm"
value="{{post.id}}"> Unlike </button>
{% else %}
<button id='like' type='submit' name='post_id' class= "btn btn-primary btn-sm"
value="{{post.id}}"> Like </button>
{% endif %}
{% else %}
<p><small> Login to Like </small></p>
{% endif %}
</form>
here is the ajax
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function(event){
$(document).on('click','#like', function(event){
event.preventDefault();
$var pk= $(this).attr('value');
$.ajax({
type:'POST',
url:'{% url "score:like_post" post.pk %}', <---- Error in this line
data:{'id': pk, 'csrfmiddlewaretoken':'{{csrf_token}}'},
dataType:'json',
success:function(response){
$('#like-section').html(response['form'])
console.log($('#like-section').html(response['form']));
},
error:function(rs, e){
console.log(rs.responseText);
},
});
});
});
</script>

You need to send the post object in as context.
class PostDetailView(DetailView):
model = Post
template_name = "post_detail.html"
def get_context_data(self, *args, **kwargs):
context = super(PostDetailView, self).get_context_data()
post = get_object_or_404(Post, id=self.kwargs['pk'])
content["post"] = post
total_likes = post.total_likes()
liked = False
if post.likes.filter(id=self.request.user.id).exists():
liked = True
context["total_likes"] = total_likes
context["liked"] = liked
return context

Related

I added a Like model but I don't know how to increase the number of like

I created a Blog app, and i want to add a Like button to each post in the Blog, how can i do this ?
how can i make this happen in view and the template ?
the models:
class Like(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Photo, on_delete=models.CASCADE)
def __str__(self):
return str(self.user)
the view:
def viewPhoto(request, pk):
post = get_object_or_404(Photo, id=pk)
photo = Photo.objects.get(id=pk)
return render(request, 'photo.html', {'photo': photo, 'post': post })
In other to use the like functionality you do something like this, but i hope you know how to work the frontend part, in your html?
def like(request, pk):
post = Photo.objects.get(pk=pk)
liked= False
like = Like.objects.filter(user=request.user, post=post)
if like:
like.delete()
else:
liked = True
Like.objects.create(user=request.user, post=post)
resp = {
'liked':liked
}
response = json.dumps(resp)
return redirect('') # redirect to any url after the object has been liked
return HttpResponse(response,content_type = "application/json")
You would need to put the liked object in any view that would prefer to have the like functionality:
def viewPhoto(request, pk):
post = get_object_or_404(Photo, id=pk)
photo = Photo.objects.get(id=pk)
liked = [i for i in Photo.objects.all() if Like.objects.filter(user=request.user, post=i)]
return render(request, 'photo.html', {'photo': photo, 'post': post ,'liked':liked})
Html template this is just to give you an idea on how to do that in your html , since i don't no what type of script or css you are using. so the button might not look like a thumb in your case.
<button class="btn btn-white mr-3 like" id="{{ post.id }}">
{% if post in liked %}
<a href="{% url 'blog:like' post.pk %}" id="likebtn{{ post.pk }}"
class="flex items-center space-x-2">
<div> Unlike</div>
</a>
{% else %}
<a href="{% url 'blog:like' post.pk %}" id="likebtn{{ post.pk }}" class="flex items-center space-x-2">
<div>Like</div>
</a>
{% endif %}
</button>

Number of Likes is not increasing in the template but it's works in admin

I follow a tutorial in youtube just to add a like button to my Blog application, but the number of likes is not increasing in the template. but its increase when I highlight a user and hit save in the admin area. I mean its working fine in the admin but not in template.
How can I set that ?
the model:
class Photo(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
category = models.CharField(max_length=30,null=True, blank=False)
image = models.ImageField(null=False, blank=False)
description = models.TextField(null=True)
date_added = models.DateTimeField(auto_now_add=True)
likes = models.ManyToManyField(User, related_name='blog_posts')
def total_likes(self):
return self.likes.count()
def __str__(self):
return str(self.category)
the view:
def like(request, pk):
post = get_object_or_404(Photo, id=request.GET.get('post_id'))
post.Likes.add(request.user)
return HttpResponseRedirect(reverse('view', args=[str(pk)]))
def viewPhoto(request, pk):
post = get_object_or_404(Photo, id=pk)
photo = Photo.objects.get(id=pk)
stuff = get_object_or_404(Photo, id=pk)
total_likes = stuff.total_likes()
return render(request, 'photo.html', {'photo': photo, 'post': post, 'total_likes':
total_likes})
the templates:
<form action="{% url 'Photo' photo.id %}" method="POST">
{% csrf_token %}
{{ total_likes }}
<button type="submit", name="post_id" value="{{ post.id }}">Touch</button>
</form>
the urls:
path('', views.login, name='login'),
path('home', views.home, name='home'),
path('view/<str:pk>/', views.viewPhoto, name='Photo'),
path('post/create', views.PostCreativeView.as_view(), name='post_create'),
path('register', views.register, name='register'),
path('comment/<str:pk>/', views.comment, name='comment'),
path('like/<str:pk>/', views.like, name='like_post'),
Well it's very simple to get the number of liked objects in your form by simple doing something like this :
# In your view add s to the post variable
def viewPhoto(request, pk):
posts = get_object_or_404(Photo, id=pk)
photo = Photo.objects.get(id=pk)
stuff = get_object_or_404(Photo, id=pk)
total_likes = stuff.total_likes()
return render(request, 'photo.html', {'photo': photo, 'posts': posts, 'total_likes':
total_likes})
{% for post in posts %}
<form action="{% url 'like_post' photo.id %}" method="POST">
{% csrf_token %}
{{ post.likes.count }} # this would count and give you the total number of likes
<button type="submit", name="post_id" value="{{ post.id }}">Touch</button>
</form>
{% endfor %}
# OR
{% for post in posts %}
<form action="{% url 'like_post' photo.id %}" method="POST">
{% csrf_token %}
{{ total_likes }} # this would count and give you the total number of likes
<button type="submit", name="post_id" value="{{ post.id }}">Touch</button>
</form>
{% endfor %}

Django Error: NoReverseMatch at / Reverse for 'like-post' not found

I'm developing a simple blog with Django. I tried adding ajax functionality to my like button. But got this error:
Reverse for 'like-post' with arguments '('',)' not found. 1 pattern(s)
tried: ['like/(?P[0-9]+)$']
PS: I followed this video to create a like button and this video add ajax functionality
views.py
class PostDetailView(FormMixin, DetailView):
model = Post
form_class = CommentForm
def get_success_url(self):
return reverse('post-detail', kwargs={'pk': self.object.id})
def get_context_data(self, **kwargs):
context = super(PostDetailView, self).get_context_data(**kwargs)
postid = get_object_or_404(Post, id=self.kwargs['pk'])
total_likes = postid.total_likes()
context['form'] = CommentForm(initial={'post': self.object})
context['total_likes'] = total_likes
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
form.save()
return super(PostDetailView, self).form_valid(form)
def LikeView(request, pk):
post = Post.objects.get(id=pk)
#post = get_object_or_404(Post, id=request.POST.get('post-id'))
post.likes.add(request.user)
context = {
'post': post,
'total_likes': post.total_likes,
}
if request.is_ajax():
html = render_to_string('blogsite/like_section.html', context, request=request)
return JsonResponse({'form': html})
return HttpResponseRedirect(reverse('blogsite-home'))
urls.py
urlpatterns = [
path('', PostListView.as_view(), name='blogsite-home'),
path('post/<int:pk>/', PostDetailView.as_view(), name='post-detail'),
path('post/new/', PostCreateView.as_view(), name='post-create'),
path('post/<int:pk>/update/', PostUpdateView.as_view(), name='post-update'),
path('post/<int:pk>/delete/', PostDeleteView.as_view(), name='post-delete'),
path('like/<int:pk>', LikeView, name='like-post'),
]
base.html
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function(event){
$(document).on('click', '#like', function(event){
event.preventDefault();
var pk = $(this).attr('value');
$.ajax({
type: 'POST',
url: '{% url "like-post" post.pk %}',
data: {'id':pk, 'csrfmiddlewaretoken':'{{ csrf_token }}'},
dataType: 'json',
success: function(response){
$('#like-section').html(response['form'])
console.log($('#like-section').html(response['form']));
},
error: function(rs, e){
console.log(rs.responseText);
}
});
});
});
</script>
like_section.html
<h5><form method="POST" action="{% url 'like-post' post.id %}">
{% csrf_token %}
<button type="submit" id="like" class="btn btn-link btn-sm" name="post-id" value="{{ post.id }}"><strong><i class="fa fa-thumbs-up"></i> {{ post.total_likes }}</strong></button><h6 align ="right" class="d-inline-block">
<i class="fa fa-facebook-f"></i> <i class="fa fa-linkedin"></i>
</h6></form>
like_section.html is then included in my post_detail.html
<div id="like-section">
{% include 'blogsite/like_section.html' %}
</div>
I can't seem to find a solution to this problem.
If believe error comes out when you try to load post detail view. You're missing post variable in get_context_data of the PostDetailView. And as you're trying to access post.pk in {% url "like-post" post.pk %}. When Django does not find variable in context, the template system inserts the value of the engine’s string_if_invalid configuration option, which defaults to ''(empty string). As a result, url tag tries to lookup for url which is like/<string:pk> which is not defined in your urls.py and thus outputs error. So, what actually you have to do is to add post to context in get_context_data.

How to hide html button if queryset doesn't have next blog post in Django?

I would like to hide the Next button in my blog if there is no next blog post available.
Is there some kind of built-in method to check has_next has_previous or do I need to create some logic in my view and extend the template with let's say {% if Foo %} show button {% endif %} ?
views.py
def render_post(request, id):
category_count = get_category_count()
most_recent = Post.objects.order_by('-timestamp')[:3]
post = get_object_or_404(Post, id=id)
next_post_id = int(id) + 1
previous_post_id = int(id) - 1
PostView.objects.get(post=post)
context = {
'post': post,
'id': id,
'next_post_id': next_post_id,
'previous_post_id': previous_post_id,
'most_recent': most_recent,
'category_count': category_count,
}
return render(request, 'post.html', context)
html
<div id="button-wrapper">
<button class="buttons" type="submit">Previous</button>
<button class="buttons" type="submit">Next</button>
</div>
You could do it by checking that the previous/next posts exist and returning the results in the context:
views.py
def render_post(request, id):
category_count = get_category_count()
most_recent = Post.objects.order_by('-timestamp')[:3]
post = get_object_or_404(Post, id=id)
next_post_id = int(id) + 1
previous_post_id = int(id) - 1
try:
previous_post_exists = Post.objects.filter(id=previous_post_id).exists()
except Post.DoesNotExist:
previous_post_exists = False
try:
next_post_exists = Post.objects.filter(id=next_post_id).exists()
except Post.DoesNotExist:
next_post_exists = False
context = {
'post': post,
'id': id,
'next_post_id': next_post_id,
'previous_post_id': previous_post_id,
'previous_post_exists': previous_post_exists,
'next_post_exists': next_post_exists,
'most_recent': most_recent,
'category_count': category_count,
}
return render(request, 'post.html', context)
You would then need to check those values in your template:
html
<div id="button-wrapper">
{% if previous_post_exists %}
<button class="buttons" type="submit">Previous</button>
{% endif %}
{% if next_post_exists %}
<button class="buttons" type="submit">Next</button>
{% endif %}
</div>

Unable to log in to the PostCreateView template

I have built a blog project and I can't access the PostCreateView for some unknown reason.
I keep getting Page 404 error although I have in the urls patterns
I am also getting the error raised by
score.views.PostDetailView
Here is the views.py
class PostCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
model = Post
fields = ['title', 'design']
template_name = "post_form.html"
success_url = "/score/"
success_message = "Your Post has been submitted"
def form_valid(self, form):
form.instance.designer = self.request.user
return super().form_valid(form)
here is the PostDetail Views.py
class PostDetailView(DetailView):
model = Post
template_name = "post_detail.html"
def get_context_data(self, *args, **kwargs):
context = super(PostDetailView, self).get_context_data()
post = get_object_or_404(Post, slug=self.kwargs['slug'])
total_likes = post.total_likes()
liked = False
if post.likes.filter(id=self.request.user.id).exists():
liked = True
context["total_likes"] = total_likes
context["liked"] = liked
return context
Here is the Urls.py
app_name = 'score'
urlpatterns = [
path('', PostListView.as_view(), name='score'),
path('<slug:slug>/', PostDetailView.as_view(), name='post-detail'),
path('new/', PostCreateView.as_view(), name='post-create'),
path('user/<str:username>', UserPostListView.as_view(), name='user-posts'),
]
here is the nav bar html that is taking me to the page
<a class="nav-link waves-effect" href="{% url 'score:post-create' %}" >Upload Post</a>
here is the post_form template
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4" style="padding-top: 20px;">Upload Your Post</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info ml-0" type="submit">Upload</button>
</div>
</form>
The problem is that your <slug:slug> will capture values like new as well. So that path will "fire", and the view will find out that there is no Post object with 'new' as slug.
You can swap the path(…)s, but then you can never access a Post with as slug new. As a rule of thumb it is better to make path(…)s that are not overlapping, for example:
app_name = 'score'
urlpatterns = [
path('', PostListView.as_view(), name='score'),
path('post/<slug:slug>/', PostDetailView.as_view(), name='post-detail'),
path('new/', PostCreateView.as_view(), name='post-create'),
path('user/<str:username>', UserPostListView.as_view(), name='user-posts'),
]