The usecase of my app is to show a list of furnitures in the homepage. There is quick preview button in all those furnitures which when clicked should show its detail information. I tried to use ajax for that.
If i click on the furniture quick preview button, I get the clicked furniture slug from which I do the query and get that furniture detail information. Upto this, its working and also the modal is shown but could not show the content. How can i now show the content in the modal-body?
Here is what I have tried
def ajax_furniture_detail(request):
furniture_slug = request.GET.get('slug', None)
qs = Furniture.objects.get(slug=furniture_slug)
cart_obj, new_obj = Cart.objects.new_or_get(request)
context = {
'furniture': model_to_dict(qs),
'cart': model_to_dict(cart_obj),
'status': 'ok'
}
return JsonResponse(context)
{% block content %}
{% include 'furnitures/furnitures_home.html'%}
{% endblock content %}
{% block js %}
{{ block.super }}
<script>
$(document).ready(function(){
$('.quick-view-button').click(function() {
var _this = $(this);
var slug = _this.attr("data-slug");
$.ajax({
url: '/ajax/furniture',
type: "get",
data: {'slug': slug},
success: function(data) {
$('#product-quick-view-modal').modal('show');
$('#product-quick-view-modal').find('.modal-body').html(data.html);
},
error: function(err) {
console.log('error', err);
}
})
})
});
</script>
{% endblock js %}
furnitures_home.html
{% load static %}
<div class="furnitures" id="content">
{% for furniture in furnitures %}
{% if forloop.first %}<div class="row products">{% endif %}
<div class="col-md-4 col-sm-3">
<div class="product">
<div class="image" style="height: 205px;">
<div class="quick-view-button" data-slug={{ furniture.slug }}>
Quick view
</div>
{% endif %}
</div>
</div>
</div>
{% endfor %}
</div>
<div class="modal fade" id="product-quick-view-modal" tabindex="-1" role="dialog" aria-hidden="false" style="display: none;">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-body">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<p>Hello</p>
<p>{{furniture.name}}</p>
</div>
</div>
</div>
<!--/.modal-dialog-->
</div>
One neat way to do this is to use a snippet html for product detail and send the product detail html snippet using render_to_string and just replace that html snippet in the modal.
rendered = render_to_string('product_detail_snippet.html', context,
context_instance=RequestContext(request))
return JsonResponse({'product_snippet': rendered})
And in the ajax success:
$('#product-quick-view-modal').find('.modal-body').html(data.product_snippet);
Related
I am trying to make it possible to comment on the site. If I post the first comment, it posts fine. If posting the second one, fetch sends 2 requests and displays the same comment 2 times, the third comment 3 requests and displays 3 times, etc. What do I need to do for everything to work?
js
function insertNewComment(data) {
var ul = document.getElementById("new-comment");
ul.insertAdjacentHTML('afterBegin', '<li class="list-group-item">' + data.text + '</li>');
}
function sendComment() {
document.getElementById('comment').addEventListener('submit', (e) => {
e.preventDefault();
const formData = new FormData(e.target);
fetch(e.target.getAttribute('action'), {
method: e.target.getAttribute('method'),
body: formData
})
.then((response) => {
return response.json();
})
.then((data) => {
insertNewComment(data);
document.getElementById('comment').reset();
});
});
}
html
{% extends 'base.html' %}
{% block other_resources %}
<link rel="stylesheet" href="{{ url_for('.static', filename='css/post.css') }}">
<script src="{{ url_for('.static', filename='js/post.js') }}"></script>
{% endblock %}
{% block content %}
<div class="card">
<img src="{{ post.photo_path }}" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">{{ post.title }}</h5>
<p class="card-text">{{ post.text }}</p>
<p class="card-text"><small class="text-muted">{{ post.created }}</small></p>
<div class="card-header">
Написать комментарий:
</div>
<div>
<form action="{{ url_for('news.add_comment', post_id=post.id) }}" id='comment' method="POST">
{{form.csrf_token}}
<div class="form-floating">
{{form.text(class='form-control')}}
{{form.text.label(for="floatingTextarea2")}}
</div>
{{form.submit(class="btn btn-dark send-comment", onclick='sendComment()')}}
<div class="form-floating">
<label for="floatingTextarea2">Комментарий</label>
</div>
</form>
</div>
<div class="card comments">
<div class="card-header">
Комментарии:
</div>
<ul class="list-group list-group-flush" id='new-comment'>
{% for c in comments %}
<li class="list-group-item">{{ c.text }}</li>
{% endfor %}
</ul>
</div>
</div>
</div>
{% endblock %}
Every time you execute sendComment you add another submit listener to that form.
Make sure you only add one listener:
function sendComment(e) {
e.preventDefault();
const formData = new FormData(e.target);
fetch(e.target.getAttribute('action'), {
method: e.target.getAttribute('method'),
body: formData
})
.then((response) => {
return response.json();
})
.then((data) => {
insertNewComment(data);
document.getElementById('comment').reset();
});
};
// add the listener outside of `sendComment`
document.getElementById('comment').addEventListener('submit', sendComment);
Let me start by saying I have 2 variables in an HTML template(messages and users) and I have multiple buttons that when one of them is clicked it calls a jquery code that sends a post request to a Django server and it returns an update to a variable(messages)
however, it's not updating the loop, I also tried to return a new HTML page that contains the new variable updated but the jquery is not updating the whole page with the new HTML
if I can update the variable alone it would be better and if I can't do that how can I make jquery use the new HTML page
the python code i used to return the update to the varialbe messages:
if request.method == 'POST':
send=Message.objects.filter(from_id=request.POST.get('userId'),to_id=2)
rec=Message.objects.filter(from_id=2,to_id=request.POST.get('userId'))
messages=sorted(chain(rec, send),key=lambda instance: instance.id,reverse=True)
print(messages)
return HttpResponse(list(messages))
and the code i used to return new HTML template:
m = Message.objects.filter(to_id=2).order_by('-id')
users = {}
for i in m:
if users.get(i.from_id.username) == None:
users[i.from_id.username] = User.objects.get(id=i.from_id.id)
users = list(users.values())
send=Message.objects.filter(from_id=users[0].id,to_id=2)
rec=Message.objects.filter(from_id=2,to_id=users[0].id)
messages=sorted(chain(rec, send),key=lambda instance: instance.id,reverse=True)
if request.method == 'POST':
send=Message.objects.filter(from_id=request.POST.get('userId'),to_id=2)
rec=Message.objects.filter(from_id=2,to_id=request.POST.get('userId'))
messages=sorted(chain(rec, send),key=lambda instance: instance.id,reverse=True)
print(messages)
return render(request,'psych.html',{"users":users, "messages":list(messages)})
return render(request,'psych.html',{"users":users, "messages":list(messages)})
the HTML code and jquery code that uses the variable and try to update it
function newUser(id){
$.ajax({
type: 'POST',
url:'/psych.html/',
data:{
userId:id,
},
success: function(data){
console.log(data);// the data returnd are correct and as needed
//but i cant make it update the messages
$('#messageDiv').load(document.URL + ' #messageDiv');
}
})
}
{% for i in users %}
<li class="">
<button type="button" class="btn" onClick="newUser({{i.id}})">
<div class="d-flex bd-highlight">
<div class="img_cont">
<!-- here was an image ----------------------------------------------->
</div>
<div class="user_info">
<span>{{i.id}}</span>
</div>
</div>
</button>
</li>
{% endfor %}
<!-- The varialbe that i'm trying to update is called messages bottom -->
{% for o in messages %}
{% if o.to_id.id != 2 %}
<div class="d-flex justify-content-start mb-4">
<div class="img_cont_msg">
<!-- here was an image-->
</div>
<div class="msg_cotainer">
{{o.message}}
<!-- <span class="msg_time">{{o.time}}</span> -->
</div>
</div>
{% else %}
<div class="d-flex justify-content-end mb-4">
<div class="msg_cotainer_send">
{{o.message}}
<!-- <span class="msg_time_send">{{o.time}}</span> -->
</div>
<div class="img_cont_msg">
<!-- here was an image-->
</div>
</div>
{% endif %}
{% endfor %}
if it helps i did it before and updated the messages from jquery but i used form and there was only 1 variable i will add the code to that too
$(document).on('submit','#submitMessage', function (e){
e.preventDefault();
$.ajax({
type: 'POST',
url:'/psych.html/',
data:{
message:$('#messageHolder').val(),
csrfmiddlewaretoken: $('input[message=csrfmiddlewaretoken]').val(),
},
success: function(data){
// it work like charm here
$('#messageDiv').load(document.URL + ' #messageDiv');
}
})
})
{% for o in messages %}
{% if o.to_id.id == 2 %}
<div class="d-flex justify-content-start mb-4">
<div class="img_cont_msg">
<!-- here was an image-->
</div>
<div class="msg_cotainer">
{{o.message}}
<!-- <span class="msg_time">{{o.time}}</span> -->
</div>
</div>
{% else %}
<div class="d-flex justify-content-end mb-4">
<div class="msg_cotainer_send">
{{o.message}}
<!-- <span class="msg_time_send">{{o.time}}</span> -->
</div>
<div class="img_cont_msg">
<!-- here was an image-->
</div>
</div>
{% endif %}
{% endfor %}
<form id="submitMessage" >
{% csrf_token %}
<div class="card-footer">
<div class="input-group">
<div class="input-group-append"></div>
<input name="message" class="form-control type_msg" placeholder="Type your message..." id="messageHolder">
<div class="input-group-append">
<button type="submit" class="btn">
<span class="input-group-text send_btn" ><i class="fas fa-location-arrow"></i></span>
</button>
</div>
</div>
</div>
</form>
Try this
$("#messageDiv").load(location.href+" #messageDiv>*");
i figured the problem and it was because i didn't know that
$("#messageDiv").load(location.href+" #messageDiv>*");
would make a GET request so all I did was adding the necessary data to the URL and then change the URL too(so if the client refreshed the page it would stay in the same spot) without refreshing the page and then do the command app there
if it could help anyone please look at the code below:
function newUser(id){
var url = document.URL;
url = url.split('/');
url[url.length-2] = id;
url = url.join('/');
window.history.pushState("object or string", "my website name", url);
$('#messageDiv').load(url + ' #messageDiv');
}
sadly i don't know how to do post requst then load the page please if you know comment down bellow so someone else might get help from it
I am using Django 3.2
I am creating a simple newsletter subscription form. The form submission returns JSON to the frontend, which should then be used to update parts of the page - however, when I post the form, the JSON string is displayed as text on a new page.
Here is the route that calls the view:
urlpatterns = [
# ...
path('subscription', BlogsubscriberCreateView.as_view(), name='subscription-post'),
# ...
]
Here is my class based view:
class BlogsubscriberCreateView(CreateView):
model = BlogPostSubscriber
form_class = BlogSubscriptionForm
http_method_names = ['post']
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
content_type = "application/json"
if not form.is_valid():
return JsonResponse({'ok': 0, 'msg': form.errors.get('email')[0]}, content_type=content_type, status=200)
else:
email = form.cleaned_data.get("email")
subscriber = BlogPostSubscriber(email= email)
subscriber.save()
# send email to confirm opt-in
email_message='Please confirm your subscription'
message = f"A confirmation email has been sent to {email}. Please confirm within 7 days"
return JsonResponse({'ok': 1, 'msg': message}, content_type=content_type, status=200)
Here is a snippet of the HTML containing the form:
<div class="col-lg-8 content">
<form id="blog-subscription" action="{% url 'blog:subscription-post' %}" method="post">
{% csrf_token %}
<br />
<h3>Some title</h3>
<br />
<p>Lorem ipsum ...</p>
<br />
<h4 id='submit-response-h4'>SUBSCRIBE TO OUR BLOG</h4>
<div id="submit-response" class="input-group">
<span id="email-error"></span>
<input type="email" id="blog-subscription-email" name="email" class="form-control" placeholder="Enter your email" required="true">
<span class="input-group-btn">
<button id="subscribe-btn" class="btn" type="submit">Subscribe Now</button>
</span>
</div>
</form>
Here is the Javascript that is responsible for updating the page:
$().ready(function() {
$('form#blog-subscription button#subscribe-btn.btn').on('click', function(e){
let email = $('#blog-subscription-email').val().trim().toLowerCase();
if (email.length && isValidEmail(email)){
e.preventDefault();
$.post({
url: "{% url 'blog:subscription-post' %}",
dataType: 'json',
data: {
email: email,
csrfmiddlewaretoken: '{{ csrf_token }}'
},
success: function (data){
let ok = data.ok;
if (ok){
if ($('#submit-response-h4').length){
$('#submit-response-h4').remove();
}
$('#submit-response').text(data.msg);
}
else{
$('#email-error').text(data.msg);
}
}
});
}
});
});
I placed an alert note in my Javascript and realised that it is simply not being called at all. I don't understand what is going on - if the Javascript is not being called - how does JQuery know to call the post function to the correct URL? (form has no action attribute!).
Typical example of a response (with a bad email) shown at http://example.com/path/to/subscription:
{"ok": 0, "msg": "Please use a different email service provider"}
What is causing this problem - and how do I fix it?
This code populates a modal with relevant images. It is not exactly the same as your issue, but this does work so should be transferable with amendments.
js
function modal_form(form_url){
var modal_id = 'modal-form';
$.ajax({
type: 'GET',
url: form_url,
cache: false,
success: function (data, status) {
$('#carouselModal').html(data);
$('#carouselModal').modal('show');
}
});
}
html
<img class="d-block w-100" src="{% thumbnail image.file 'carousel_main' subject_location=image.file.subject_location %}" alt="{{ organisation.name }} - {{ forloop.counter }}" itemprop="image" onclick="modal_form('{% url "providers:carousel-modal" organisation.id "carousel_photos" %}')">
urls
path('carousel-modal/<int:pk>/<str:gallery>/', views.CarouselModal.as_view(), name="carousel-modal"),
views
class CarouselModal(DetailView):
model = models.Organisation
template_name = "providers/extras/gallery_modal.html"
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
gallery = self.kwargs['gallery']
if gallery == 'carousel_photos':
ctx['images'] = self.object.carousel_photos
elif gallery == 'propertyimages':
ctx['images'] = self.object.propertyimages_set.all()
if self.request.user_agent.is_mobile:
ctx['image_size'] = 'mobile_modal_carousel_slide'
elif self.request.user_agent.is_tablet:
ctx['image_size'] = 'tablet_modal_carousel_slide'
else: ctx['image_size'] = 'modal_carousel_slide'
try: ctx['show_all'] = self.object.is_premium
except: ctx['show_all'] = False
return ctx
gallery_modal.html
{% load static thumbnail %}
<div class="modal-dialog carousel-modal" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div id="mainCarousel" class="carousel slide thumbnail-carousel" data-ride="carousel">
<div class="thumbnail-carousel__main">
<div class="carousel-inner">
{% for image in images %}
{% if show_all or forloop.first %}
<div class="carousel-item{% if forloop.first %} active{% endif %}">
<img class="d-block w-100" width="100%" src="{% thumbnail image.file image_size subject_location=image.file.subject_location %}" alt="{{ image.name }}" itemprop="image" >
</div>
{% endif %}
{% empty %}
{% include 'providers/detail/stock_carousel.html' %}
{% endfor %}
</div>
{% if show_all and images.count > 1 %}
<a class="carousel-control-prev" href="#mainCarousel" role="button" data-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="carousel-control-next" href="#mainCarousel" role="button" data-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
{% endif %}
</div>
</div>
</div>
</div>
</div>
I've just realised in this example I'm not return a JSONResponse, but again, I think the principle is good
What this code is supposed to do is let a user click they're description and be able to edit it. I have the modal popping up, but the save button will not save the data and produces the following error:
Uncaught ReferenceError: csrftoken is not defined
at HTMLButtonElement.<anonymous> (modalShortListDescription.js:6)
at HTMLButtonElement.dispatch (jquery.min.js:3)
at HTMLButtonElement.r.handle (jquery.min.js:3)
Here's where the modal is called:
<div class="tab-content col-xs-12">
{% for list in lists %}
<input type="hidden" id="idList" id_list="{{list.id}}">
{% if forloop.first and not createTabActive %}
<div role="tabpanel" class="tab-pane fade active in" id="list{{list.id}}">
{% else %}
<div role="tabpanel" class="tab-pane fade" id="list{{list.id}}">
{% endif %}
<div class="content col-xs-12">
<div class="form-horizontal sort-by col-xs-12">
<h3>Description</h3>
{% if list.description %}
{{list.description}}
{% else %}
None
{% endif %}
{% include "layout/popUp/modal-short-list-description.html" %}
</div>
Here's the modal itself:
<div class="modal fade" id="modalDescription{{list.id}}" role="dialog">
<div class="modal-dialog">
<form class="form-horizontal" action="{% url 'update-list-description' %}" method="post">
{% csrf_token %}
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Description</h4>
</div>
<div class="modal-body modal-body-exper modal-body-value modal-body-t">
<div class="lineEnterValue lineTeamSize lineTitle">
<div class="form-group {% if form.description.errors %} has-error{% elif form.is_bound %} has-success{% endif %}">
<div class="col-sm-10">
<textarea name="{{ form.description.html_name }}" class="form-control" id="{{ form.description.id_for_label }}" rows="5" style="margin: 0px; height: 90px; width: 455px;"></textarea>
</div>
{% if form.description.errors %}
<ul class="col-sm-10 col-sm-offset-2 error-list text-danger">
{% for error in form.description.errors %}
<li>{{ error|escape }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
</div>
</div>
<div class="modal-footer modal-footer-value">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary" id="description_save">Save</button>
</div>
</div>
</form>
</div>
Here is the .js that the save button uses:
$(document).ready(function() {
$("#description_save").click(function() {
var description = $("#form.description").val();
var idList = $("#idList").attr("id_list");
var url = "/bid/update-list-description";
csrftoken();
$.ajax({
type: "POST",
url: url,
data: {description : description, idList: idList},
}).done(function(response){
$(".modalDescription").modal("hide");
$(".editDescription").text(description);
});
})
})
EDIT:
views.py:
#csrf_protect
def updateListDescription(request):
checkEmployer(request)
pageClass="my-short-lists search-for-prospect"
#shortList = get_object_or_404(List, id = request.POST.get("idList"))
shortList = request.user.profile.profile_employers.employer_lists.filter(pk=request.POST.get("idList"))
if request.method =="POST":
form = ListForm(request.POST)
if form.is_valid():
shortList.description = form.cleaned_data["description"]
shortList.save()
else:
form = ListForm()
return redirect('my-short-lists')
EDIT:
I think the problem lies in not only csrftoken, but also in the button: if a button calls ajax, it should not be submit. If it posts the form, it should not do ajax call. It seems that you add the token in the form, but ajax does his thing first... So the first answer seems valid.
Or,
You can instead add a header to every ajax call in $.ajaxSetup(). The DOC has this part explained:
Define the getCookie(name) method;
Define var csrftoken = getCookie('csrftoken');;
Use these lines:
That is:
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
Then you don't have to change any ajax call. The header is attached in every ajax call.
https://docs.djangoproject.com/en/2.0/ref/csrf/, under "Ajax" section.
I have used this approach and it worked.
The AJAX POST doesn't include the csrf_token. Add:
'csrfmiddlewaretoken': $('[name="csrfmiddlewaretoken"]').val()
into $.ajax data (along with description and idList) and remove csrftoken().
Edit your js to this
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
$(document).ready(function() {
$("#description_save").click(function() {
var description = $("#form.description").val();
var idList = $("#idList").attr("id_list");
var url = "/bid/update-list-description";
var csrftoken = getCookie('csrftoken');
$.ajax({
type: "POST",
url: url,
data: {description : description,
idList: idList,
csrfmiddlewaretoken: csrf_token
},
}).done(function(response){
$(".modalDescription").modal("hide");
$(".editDescription").text(description);
});
})
})
Hi I create my first project like stackoverflow(question-answer). I used this guid from Tango with Django http://www.tangowithdjango.com/book17/chapters/ajax.html to add like button with ajax. And nothing hapened. Don't see any request in console. I'm noob in Django, and it's my first encounter with jquery.
apps/questions/models:
class Answer(models.Model):
text = models.TextField()
date = models.DateTimeField(default=datetime.datetime.now)
likes = models.IntegerField(default=0)
resolve = models.IntegerField(default=0)
author = models.ForeignKey(CustomUser)
question = models.ForeignKey(Question)
apps/questions/views:
#login_required
def add_like(request):
ans_id = None
if request.method == 'GET':
ans_id = request.GET['answer_pk']
likes = 0
if ans_id:
ans = Answer.objects.get(id=(int(ans_id)))
if ans:
likes = ans.likes + 1
ans.likes = likes
ans.save()
return HttpResponse(likes)
apps/questions/ulrs:
url:
url(r'add_like/$', views.add_like, name='add_like'),
question.html:
{% for answer in answers %}
<div class="container-fluid no-padding">
{{ answer.text }}
</div>
<div class="container-fluid author-question">
<p>posted: {{ answer.date.day|stringformat:"02d" }}.{{ answer.date.month|stringformat:"02d"}}.{{ answer.date.year}}</p>
<p>by: {{ answer.author.username }}</p>
</div>
{% if user.is_authenticated %}
<button class="btn btn-default" type="button" id="likes" data-ansid="{{ answer.id }}">
like | <strong id="like_count">{{ answer.likes }}</strong>
</button>
{% endif %}
js/ajax.js:
$('#likes').click(function(){
var ansid;
ansid = $(this).attr("data-ansid");
$.get('/questions/add_like/', {answer_id: ansid}, function(data){
$('#like_count').html(data);
$('#likes').hide();
});
});
Since you are creating buttons in a for loop, and naming them the same way, you have multiple elements on the page with the same id. Because of this you get unpredictable results. You should either give each button its own id, or change the jQuery selector to select the buttons based on the appropriate class.
For example, you could have:
{% for answer in answers %}
<div class="container-fluid no-padding">
{{ answer.text }}
</div>
<div class="container-fluid author-question">
<p>posted: {{ answer.date.day|stringformat:"02d" }}.{{ answer.date.month|stringformat:"02d"}}.{{ answer.date.year}}</p>
<p>by: {{ answer.author.username }}</p>
</div>
{% if user.is_authenticated %}
<button class="btn btn-default likes-button" type="button" data-ansid="{{ answer.id }}">
like | <strong id="like_count">{{ answer.likes }}</strong>
</button>
{% endif %}
{% endfor %}
And then for the javascript
$('.likes-button').click(function(){
var ansid;
ansid = $(this).attr("data-ansid");
$.get('/questions/add_like/', {answer_id: ansid}, function(data){
$('#like_count').html(data);
$('#likes').hide();
});
});