Reputation: 769
I am trying to make a upvote and downvote functionality on my website. however there is a particular behaviour that i don't like which is that whenever a user clicks the button, he should not be redirect to another page but should remain on the same page.
What happens after clicking the upvote button is that it goes to the url http://localhost:8001/upvote/2/
which i don't want. I want it to remain on the same page which is http://localhost:8001/view-supplier/
models.py
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=254, unique=True)
# CUSTOM USER FIELDS
firstname = models.CharField(max_length=30)
lastname = models.CharField(max_length=30)
upvotes = models.IntegerField(default=0)
downvotes = models.IntegerField(default=0)
objects = UserManager()
def get_absolute_url(self):
return "/users/%i/" % (self.pk)
def get_email(self):
return self.email
views.py
def Viewsupplier(request):
title = "All Suppliers"
suppliers = User.objects.filter(user_type__is_supplier=True)
context = {"suppliers":suppliers, "title":title}
return render(request, 'core/view-suppliers.html', context)
@login_required
def upvote(request, pk):
supplier_vote = get_object_or_404(User, id=pk)
supplier_vote.upvotes += 1
supplier_vote.save()
upvote_count = supplier_vote.upvotes
context = {"supplier_vote":supplier_vote, "upvote_count":upvote_count}
return render(request, "core/view-suppliers.html", context)
@login_required
def downvote(request, pk):
supplier_vote = get_object_or_404(User, id=pk)
supplier_vote.downvotes -= 1
supplier_vote.save()
downvote_count = supplier_vote.downvotes
context = {"supplier_vote":supplier_vote, "downvote_count":downvote_count}
return render(request, "core/view-suppliers.html", context)
urls.py
from django.urls import path
from . import views
urlpatterns = [
path('upvote/<int:pk>/', views.upvote, name='upvote'),
path('downvote/<int:pk>/', views.downvote, name='downvote'),
]
view-supplier.html
<table class="table table-borderless table-data3">
<thead>
<tr>
<th>No</th>
<th>Country</th>
<th>Votes</th>
</tr>
</thead>
<tbody>
{% for supplier in suppliers %}
<tr>
<td>{{forloop.counter}}</td>
<td>{{supplier.country}}</td>
<td>
<div class="table-data-feature">
<a href="{% url 'upvote' supplier.id %}" class="m-r-10">
<button class="item" data-toggle="tooltip" data-placement="top" title="Like">
<i class="zmdi zmdi-thumb-up"></i>{{upvote_count}}</button>
</a>
<a href="{% url 'downvote' supplier.id %}">
<button class="item" data-toggle="tooltip" data-placement="top" title="Dislike">
<i class="zmdi zmdi-thumb-down"></i>{{downvote_count}}</button>
</a>
</div>
</td>
</tr>
{% empty %}
<tr><td class="text-center p-5" colspan="7"><h4>No supplier available</h4></td></tr>
{% endfor %}
</tbody>
</table>
Upvotes: 0
Views: 70
Reputation: 1313
You need to implement an API (application programming interface) to send the upvote and downvote asynchronously. Django REST framework is the way to go to create your very own API. You can watch hours of video tutorials on that subject on YouTube. The docs for Django REST framework is really great and easy to read as well. Django is a server-side web-framework which means that it can only help you if you submit to the server. You can definitely reload the same page:
return HttpResponseRedirect(reverse('<app_name>:<url_name>'))
However, there will be an interruption. So, the recommended way to handle this type of behavior is through asynchronous call using JavaScript's APIs such as the Fetch API to the REST framework.
If, for probably misplaced fear of learning asynchronous coding, you decide to send data to your server the old-fashion way, you can always use your upvote and downvote to submit user data and update the counts. Then, in your view_supplier view, you need to get the updated view count and pass it to the context. So, your upvote view changes the upvote counts and trigger the Viewsupplier view. Then, inside the ViewSupplier view, you get the counts and add it to the context
# in your template
<a href="{% url 'upvote' supplier.id %}" class="m-r-10">
<button class="item" data-toggle="tooltip" data-placement="top" title="Like">
<i class="zmdi zmdi-thumb-up"></i>{{upvote_count}}</button>
</a>
# in your view
def Viewsupplier(request):
title = "All Suppliers"
suppliers = User.objects.filter(user_type__is_supplier=True)
# Get the updated count:
suppliers_votes_count = {}
for supplier in suppliers:
upvote_count = supplier.upvotes
downvote_count = supplier.upvotes
supplier_count = {supplier: {'upvote': upvote_count, 'downvote': downvote_count } }
suppliers_votes_count.update(supplier_count)
context = {"suppliers":suppliers, "title":title, "suppliers_votes_count": suppliers_votes_count }
return render(request, 'core/view-suppliers.html', context)
@login_required
def upvote(request, pk):
supplier_vote = get_object_or_404(User, id=pk)
supplier_vote.upvotes += 1
supplier_vote.save()
upvote_count = supplier_vote.upvotes
context = {"supplier_vote":supplier_vote, "upvote_count":upvote_count}
return HttpResponseRedirect(reverse('core:view_supplier'))
Upvotes: 2
Reputation: 959
Here's how I would achieve this:
first I'd have simple forms handling the upvotes/downvotes:
<form method="POST" action="{% url 'view-supplier' %}"> //upvote form
{% csrf_token %}
<input type="hidden" name="upvote-button">
<button type="submit" style="width:100%">Upvote Button</button>
</form>
<form method="POST" action="{% url 'view-supplier' %}"> // downvote form
{% csrf_token %}
<input type="hidden" name="downvote-button">
<button type="submit" style="width:100%">Downvote Button</button>
</form>
Then I'd have the view set up like:
def supplierView(request):
supplier_vote = get_object_or_404(User, id=pk)
if 'upvote-button' in request.POST:
supplier_vote.upvotes += 1
supplier_vote.save()
upvote_count = supplier_vote.upvotes
elif 'downvote-button' in request.POST:
supplier_vote.downvotes -= 1
supplier_vote.save()
downvote_count = supplier_vote.downvotes
else:
downvote_count = supplier_vote.downvotes
upvote_count = supplier_vote.upvotes
context = {
'upvote_count': upvote_count,
'downvote_count': upvote_count,
}
return render(request, 'main/view-suppliers.html', context)
This way when someone clicks the upvote button, they're redirected to the same view, which then handles adding the upvotes and updates the context.
If view-supplier
is a detail view, you'll also have to pass in the PK to the view.
Upvotes: 1
Reputation: 4789
This can be easily achieved by using AJAX.
Rather than providing the supplier_id
as part of URL, it would be better to send the supplier_id
in AJAX to your Django Upvote view. You could do like this.
HTML:
<button id="{{ supplier_id }}" class="item upvote" data-toggle="tooltip" data-placement="top" title="Like">
<i class="zmdi zmdi-thumb-up"></i>{{upvote_count}}
</button>
Extract the supplier_id
from the button on click event using Javascript.
JavaScript:
$('.upvote').on('click', function () {
var supp_id = $(this).attr('id');
$ajax({
type: 'POST',
url: '/upvote/',
data: {
supplier_id: supplier_id,
},
success: function (data) {
if (data.status == 'success') {
/* Your upvote logic like updating the color of button or showing Unlike etc.,*/
}
}
});
});
Change your url pattern in Django urls.py to
urlpatterns = [path('upvote/', views.upvote, name='upvote'),
Views.py
def upvote(request):
if request.method == 'POST':
supplier_id = request.POST['supplier_id']
# Your DB update logic goes here....
return JsonResponse({'status': 'success'})
else:
return JsonResponse({'status': 'Error'})
Similarly you could do the same for downvote
.
Upvotes: 1