In my django project, I built a little like-button. The problem is, that I had it only when I take a detailed view on a post, and now want to put it on the home page, where multiple posts are shown. The problem the Like Function of the button returns to the detailed page, but I want to make the return dependent from the url where the like came from, so that I can just scroll ahead on the home page or what ever page am on, without being returned to another page. So here is my views.py Like function:
def PostLike(request, pk):
post = get_object_or_404(Post, id=request.POST.get('post_id'))
if post.likes.filter(id=request.user.id).exists():
post.likes.remove(request.user)
else:
post.likes.add(request.user)
return HttpResponseRedirect(reverse('post-detail', args=[str(pk)]))
So in a nutshell: how could I change my Like function so that I am returned to the page I liked from?
Your function returns a Redirect, that's why you are redirected to the detail page.
If you want to stay on the same page after clicking 'Like' you could submit a request through Ajax and return a JsonResponse with a message or a value depending on what you get from the database query.
How to do this varies based on what JS library or framework you are using. Here is a simplistic JQuery example:
in views.py
from django.http import JsonResponse
from django.shortcuts import get_object_or_404
def like_or_unlike(request, id):
if request.user.is_authenticated:
try:
post = get_object_or_404(Post, id=id)
if post.likes.filter(id=request.user.id).exists():
post.likes.remove(request.user)
message = 'You unliked post ' + id
else:
post.likes.add(request.user)
message = 'You liked post ' + id
except:
message = 'Error processing like for post ' + id
else:
message = 'You must be logged in to like a post.'
return JsonResponse({ 'result': message })
in urls.py
from django.urls import path
from . import views
urlpatterns = [
...
path("like/<id>/", views.like_or_unlike),
]
in template.html
<button class="{% if post.liked %}color-blue{% else %}color-white{% endif %}"
id="post_{{ post.id|stringformat:"s" }}"
onclick="postLike( '{{ post.id|stringformat:"s" }}' )"> Like this post </button>
<script>
function postLike(id) {
var element = "#post_" + id
$.ajax({
url: '/like/' + id,
type: 'get',
contentType: 'application/json',
success: function(data) {
console.log(data);
if $(element).hasClass("color-white") {
$(element).removeClass("color-white");
$(element).addClass("color-blue");
} else {
$(element).removeClass("color-blue");
$(element).addClass("color-white");
}
},
error: function(jqXhr, textStatus, errorThrown) {
console.log(errorThrown);
}
});
}
</script>
I think you should use Ajax request for it so you don't even reload page. Just handle it in JavaScript.
Otherwise you can redirect base on request.referer value to go back to the view where click was made
Related
so i was trying to send category to django url (that takes category when visited through url) with ajax in this website, but i am getting this error which i don't seem to understand. Please help!
urls.py
urlpatterns=[
path('category/<str:category>', views.getcategory, name = 'get_category'),
]
views.py:
def get_category(request, category=None):
#To get category if requested via AJAX
if request.is_ajax and request.method == "GET":
category = request.GET.get("last_joke_id", category)
if category!=None:
return HttpResponse('\n Received via Json. \n')
else:
return HttpResponse('\n Received via URL. \n')
index.html:
<script>
function send_category(category){
$.ajax({
type: 'POST',
*url:"{% url 'get_category' %}"*, /*Error in this line*/
data: {'category':category},
success: function(response) {
console.log("\n\nCategory data Successfully recived.\n");
console.log( response );
//displayJokes(response);
}, error: function (response) {
console.log('\n\n error in retriving category data:');
console.log(response);
//return 'her';
}
})
}
</script>
Error Message:
django.urls.exceptions.NoReverseMatch: Reverse for 'get_category' with no arguments not found. 1 pattern(s) tried: ['jokes/category/(?P[^/]+)$']
It means that category is required parameter in your route, so you may fix that if you change your route and view function, like that:
urls.py
path('category/', views.getcategory, name = 'get_category'),
views.py
def getcategory(request):
category = request.GET.get("category")
if request.is_ajax and request.method == "GET":
category = request.GET.get("last_joke_id", category)
if category!=None:
return HttpResponse('\n Received via Json. \n')
else:
return HttpResponse('\n Received via URL. \n')
Or you may create two routes, with and without parameters:
path('category/', views.getcategory, name = 'get_category_default'),
path('category/<str:category>', views.getcategory, name = 'get_category'),
Then you need to set default value in your function:
def get_category(request, category=None):
...
And Finally you need to replace your get_category route to get_category_default in your js code
First of all : I already checked all the related answers and questions .... they didn't help me
so I am trying to create ajax based like button with multiple users and multiple objects or posts i tried a lot but none of them works but i steel have the base
models.py:
class BlogPost(models.Model):
#some fields
class Like (models.Model):
user = models.ForeignKey(User)
post = models.ForeignKey(BlogPost)
views.py
from .models import Like
def PostLikeToggle(request):
#here i want to capture the request check if the user liked the post or
# no by sending user.username and post.title to the endpoint like.html
#and then update his status
return render(request, 'like.html')
urls.py
from plateform import views as plateform
urlpatterns = [
#other urls
url(r'^like/', plateform.PostLikeToggle, name='PostLikeToggle'),]
like.html
{% if liked == 'false' %}
false
{% elif liked == 'true' %}
true
{% endif %}
blogpost.html
#ajax
$('.thumb').click(function () {
$.ajax({
type: 'POST',
url: '/like/',
data: {
'csrfmiddlewaretoken': $('input[name=csrfmiddlewaretoken]').val(),
},
success: LikePost,
dataType: 'html'
});
function LikePost(data, jqXHR) {
console.log(data)
}
});
UPDATE
I tried to figure it out so I added some things
models:
class BlogPost(models.Model):
#some fields
liked = models.ManyToManyField(User, related_name='PostLikeToggle')
#REMOVED class Like (models.Model)
views:
def PostLikeToggle(request):
user = request.user
if request.method == 'POST':
post_id = request.POST['post_id']
post = get_object_or_404(posts, id=post_id)
_liked = user in post.liked.all()
if _liked :
post.liked.remove(user)
else:
post.liked.add(user)
return JsonResponse({'liked':_liked})
So am I on the right way ??
okey I will just post my answer .... it was easy trick but i was little bit stingy ...
In the models file i added ManytoMany field with the relation of users ( many users + many posts)
class BlogPost(models.Model):
#more fields
liked = models.ManyToManyField(User, related_name='PostLikeToggle')
in the views i created the view which it will accept the request from the blogpost views and check if user already liked the post or no ( add the user to the liked field if liked return true remove it if .... )
def PostLikeToggle(request):
user = request.user
if request.method == 'POST':
post_id = request.POST['post_id']
post = get_object_or_404(posts, id=post_id)
_liked = user in post.liked.all()
if _liked :
post.liked.remove(user)
else:
post.liked.add(user)
return JsonResponse({'liked':_liked})
this views is associated with url where we will get the response :
url(r'^like/', plateform.PostLikeToggle, name='PostLikeToggle')
and in your blog post template you will need to link Jquery and add this Ajax function
(don't forget to customize it with your own variable class , url ... )
$('.thumb').click(function () {
$.ajax({
type: 'POST',
url: {% url 'PostLikeToggle' %},
data: {
'post_id': {{ post_slug.id }},
'csrfmiddlewaretoken': $('input[name=csrfmiddlewaretoken]').val(),
},
success: LikePost,
dataType: 'html'
});
function LikePost(data, jqXHR) {
var data = $.parseJSON(data)
if (data['liked']) {
$('.thumb').removeClass("fas fa-thumbs-up").addClass('far fa-thumbs-up')
}
else
{
$('.thumb').removeClass("far fa-thumbs-up").addClass('fas fa-thumbs-up')
}
}
});
this worked for this time if there is any exception or you have better way just post it and i will mark it
NOTICE : you have to check if the user if authenticated in the template to hide the like button or redirect him to login ....
Is it possible to create function without template?
Like I'm trying to create delete function and what I want to do is to delete something immediately after clicking the delete button and then redirect to other page.
I want to place a delete button on the page users can edit their post.
html is like this
<button type="submit" class="btn btn-outline-secondary">Save Changes</button>
</form>
and I want to place delete button next to this button.
def edit_entry(request, entry_id):
'''Edit a post.'''
entry = Entry.objects.get(id=entry_id)
if request.method != 'POST':
form = EditEntryForm(instance=entry)
else:
form = EditEntryForm(instance=entry, data=request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse_lazy('main:index'))
return render(request, 'main/edit_entry.html', {'entry': entry, 'form': form})
def delete_entry(request, entry_id):
'''Delete post'''
#I don't wanna create template for this function.
pass
Anyone who can give me tips?
by the docs simple-view you can use the httpresponse
from django.http import HttpResponse
def delete_entry(request, entry_id):
'''Delete post'''
#I don't wanna create template for this function.
return HttpResponse('done')
Usually it make sense to use ajax for this purpose.
In this case the handling of click splits into steps:
1) You click the button
2) jQuery (or any other javascript code) catches click event and sends AJAX request (usually post request, because post is used to for data modifications in REST agreement) to defined url on your server (like /delete/my/some/thing/) in urls.py
3) Djnago routes request to the delete_something view (or function)
4) delete_something takes request, checks what you want to check (like some permissions of current user), deletes what you actually want to delete and makes ajax response.
5) jQuery takes this response (which it actually waits on 3-4 steps) and checks response content to detect if server says everything is ok or there is some error.
So this is the code to create ajax request from jQuery:
$('#delete-button').click(function(){
var delete_id = (#delete-button).data()
$.ajax
({
url: '/delete/my/some/thing/',
data: {"id": delete_id},
type: 'post',
success: function(result)
{
// here you can write code to show some success messages
},
error: function(result) {
// here you can write code to show some error messages or re-send request
}
});
});
You also can use not $.ajax() method, but $.post(), if you want.
This is the code from this answer to make Django json response:
return HttpResponse(json.dumps(response_data), content_type="application/json")
It could look like this:
import json
from django.http import HttpResponse
def delete_something(request):
resp_data = {}
user = request.user
delete_id = request.POST.get('delete_id') # or None, if there is no delete_id
# here you can check user permissions, if your site has them
# for example check that user can delete something and can delete this entity by writing check_user_can_delete() function
if delete_id and check_user_can_delete(user):
# do your deletion
resp_data = {'status': 'ok'}
else:
resp_data.update(errors=[])
if no delete_id:
resp_data['errors'] = 'no delete_id'
if not check_user_can_delete(user):
resp_data['errors'] = 'you cave no permissions to delete delete_id'
resp = json.dumps(resp_data)
return HttpResponse(resp, content_type='application/json')
Also note that Django 1.7 has JsonResponse object, as shown in SO answer I mentioned.
The main page of my website has multiple buttons at the top. Whenever one of these buttons is pushed, a get request is sent to a django view, which is redirected and a queryset of django models is filtered and eventually displayed on the web page. I know that my ajax works because the terminal says the request is redirected properly. The function it redirects to also seems to be working, as it is quite simple and has not thrown any errors. However, my view remains the same and I'm not sure why.
urls.py
url(r'ajax_filter/', views.ajax_filter, name='ajax_filter'),
url(r'filter=(\w+)/$', views.filtered_index, name='filtered_index'),
views.py
def filtered_index(request, filter):
clothes = Clothes_Item.objects.filter(gender=filter)
if request.user.is_authenticated():
favorite_clothes_ids = get_favorite_clothes_ids(request)
return render(request, 'test.html', {'clothes': clothes, 'favorite_clothes_ids': favorite_clothes_ids})
else:
return render(request, 'test.html', {'clothes': clothes, })
def ajax_filter(request):
if request.is_ajax():
gender_filter = request.GET.get('gender_filter') #filter type
if gender_filter is not None:
return HttpResponseRedirect(reverse('filtered_index', args=[gender_filter]))
return HttpResponse('')
You can not use Django redirect in your case. When you send an ajax request you usually expect a json response, and based on that you can redirect the user via your JavaScript code.
$.ajax({
// You send your request here
}).done(function(data) {
// You can handle your redirection here
});
Here is how you can handle a redirect with your setup, you pass back a JsonResponse from django with the next page that you want to go to:
from django.http import JsonResponse
def ajax_filter(request):
if request.is_ajax():
gender_filter = request.GET.get('gender_filter') #filter type
if gender_filter is not None:
return JsonResponse({
'success': True,
'url': reverse('filtered_index', args=[gender_filter]),
})
return JsonResponse({ 'success': False })
In JS, you use done (or success) function to grab the URL from the passed back JsonResponse and redirect to that URL using window.location.href:
$.ajax({
// You send your request here
}).done(function (data) {
if (data.success) {
window.location.href = data.url;
}
});
I am trying to call my view from django template via an Ajax call.
I want the form object in response from view such that I can render this form via jquery in a div element.
Is it possible ? How?
This is what i tried:
home.html
function get_edit_form(button, id)
{
$.ajax({
url: "/manage/licenses/{{mls_signup_code}}/{{agent_id}}/" + id + "/",
type: "get",
data: {id: id},
success: function(response) {
console.log(response);
$("#formdiv").html({{ response.as_p }});
}
})
}
Views.py
elif request.method == "GET":
owned_license_id = request.GET.get('id', '')
form = OwnedLicenseForm(owned_license_id)
return form
I see what you are trying to do, but you cannot render the html form this way:
$("#formdiv").html({{ response.as_p }});
I think you are confusing server side rendering (Django templates) with client side rendering. Server side rendering happens when your server is processing the request, it cannot render objects produced by javascript running in the browser.
Because response is a javascript object, obtained by jquery sending an Ajax request to your url. At this time, the page has already been rendered by Django's template engine, and sent to the browser. There is no way for Django template to even be aware of this response.
I understand you want to use the as_p() method to render the form, you can do it like this:
function get_edit_form(button, id)
{
$.ajax({
url: "/manage/licenses/{{mls_signup_code}}/{{agent_id}}/" + id + "/",
type: "get",
data: {id: id},
success: function(response) {
console.log(response);
// response is form in html format
$("#formdiv").html(response);
}
})
}
# Views.py
elif request.method == "GET":
owned_license_id = request.GET.get('id', '')
form = OwnedLicenseForm(owned_license_id)
return HttpResponse(form.as_p()) # return a html str
You can accomplish this with a combination of Django and JQuery.
Step 1: Create an ultra simple form_from_ajax.html Template
The template can be as simple as {{form.as_p}}. The point is to not inherit your base template. You're simply using this form_from_ajax.html template to render the HTML of the form.
Step 2: Create a View with a slug argument that helps you get the correct form
def payment_method_ajax(request, method): # method is your slug
"""Load a dynamic form based on the desired payment method"""
options = {
'ach': ECheckForm(), # Dynamic form #1
'credit-card': CreditCardForm(), # Dynamic form #2
}
if method in options.keys():
context = {'form': options[method]}
else:
context = None
template = 'your_app_name/form_from_ajax.html'
return render(request, template, context)
Step 3: Define the AJAX url in urls.py
[...
path(
'payment-method-ajax/<slug:method>/', # notice the slug in the URL
views.payment_method_ajax,
name='payment-method-ajax'),
...]
Step 4: Update your template where you'd like the AJAX loaded form to appear
Make some buttons to have the user select an approprate form option
<button id="btn_ach" onclick="load_payment_form(this)">ACH</button>
<button id="btn_credit_card" onclick="load_payment_form(this)">Credit Card</button>
form-fields is where the dynamic form will be loaded
<form id="id-form" style="display: none;">
{% csrf_token %}
<div id="form-fields"></div>
<input type="submit" value="Save Payment Details"/>
</form>
Make sure to add slugs to your main view's context
context = {
'target': 'Add a New Payment Method',
'h1': 'Add a New Payment Method',
'ach': 'Save an ACH Profile',
'credit_card': 'Save a Credit Card Profile',
'slugs': ['ach', 'credit-card'], # Here are the slugs ****
}
Step 5: Load the form with JQuery and the button's onclick
<script>
var ach = 'ACH';
var creditCard = 'Credit Card';
var form_urls ={
ach : '{% url "payment-method-ajax" slugs.0 %}',
creditCard : '{% url "payment-method-ajax" slugs.1 %}',
}
function load_payment_form(btn) {
if(btn.innerText==ach) {
get_url = form_urls['ach'];
type = ach;
}
else if(btn.innerText==creditCard) {
console.log('Load credit card form');
get_url = form_urls['creditCard'];
type = creditCard;
}
$.get({'url': get_url}).done(
function(data) {
document.getElementById('form-fields').innerHTML = data;})
document.getElementById("id-form").style.display = "block";
}
</script>