Ajax call not updating template django - ajax

Below are the details. I am able to get updated "questions_in_topic" variable in views when select option is changed i.e. ajax call is made. Ajax call is updating the "questions_in_topic" variable based on selected value in dropdown. But these changes are not reflected in template. i.e. on template, I still get old values.
urls.py
url(r'^interview/add/questions/library', recruiter_views.add_question_library, name='add_question_library'),
views.py
def add_question_library(request):
question_topics = QuestionTopic.objects.values('question_id__description', 'topic_id__name','question_id','topic_id').order_by('topic_id__name','question_id__description')
all_topics = Topic.objects.all()
questions_in_topic = QuestionTopic.objects.values('question_id__description').order_by('question_id__description')
if request.is_ajax():
if 'topicId' in request.POST:
print("xx")
questions_in_topic = QuestionTopic.objects.filter(topic_id=request.POST['topicId']).values('question_id__description').order_by('question_id__description')
else:
print("yy")
questions_in_topic = QuestionTopic.objects.values('question_id__description').order_by('question_id__description')
print(questions_in_topic)
context = { 'question_topics': question_topics, 'all_topics': all_topics,'questions_in_topic':questions_in_topic,}
return render(request, 'recruiter/add_question_library.html', context)
add_question_library.html
<select id="topic" name="topic_list" class="form-control topic_select">
{% for topic in all_topics %}
<option data-topic="{{topic.id}}" value="{{topic.name}}">{{topic.name}}</option>
{% endfor %}
</select>
<ul class="list-unstyled">
{% for x in questions_in_topic %}
<li class="list-unstyled">{{ x.question_id__description }}</li>
{% endfor %}
</ul>
ajax
var topicId = $(".topic_select option:selected").attr("data-topic");
$(".topic_select").change(function(){
var topicId = $(".topic_select option:selected").attr("data-topic");
$.ajax({
type: "POST",
url: "{% url 'recruiter:add_question_library' %}",
data: {
topicId: topicId,
'csrfmiddlewaretoken': '{{ csrf_token }}',
},
success: function(){
// alert("Showing questions from topic " + topicId);
}
});
});

With the request coming from view after the ajax call, you can do this:
// stuff
success: function(response){
// $("taget").replaceWith($("target",response));
$("ul.list-unstyled").replaceWith($("ul.list-unstyled",response));
}
You may have multiple ul.list-unstyled in your project, I suggest that you add a unique ID to the list.

Related

Flask Ajax post returns TypeError: 'NoneType' object is not iterable for form data [duplicate]

This question already has answers here:
I'm having problems with wtforms selectfields when i use a POST with Flask
(2 answers)
Closed 4 years ago.
I have seen multiple questions on how to implement an ajax post method in flask using wtf-forms and I have read the docs. I am following this post as an example, but I receive a TypeError: 'NoneType' object is not iterable. I have validated the form data not using ajax and everything works fine. I am not sure why it is not working for the ajax request.
my form:
class PathsForm(FlaskForm):
name = StringField('Name', validators = [DataRequired()])
paths = SelectField('Path')
cbt_id = HiddenField('CBT ID', validators = [DataRequired()])
My template:
{% extends "base.html" %}
{% block content %}
<h1>Edit {{ cbt.name }} </h1>
<form action="" method="post">
{{ form.hidden_tag() }}
<table>
<tr>
<th>{{ form.name.label }}</th>
<th>{{ form.paths.label }}</th>
<tr>
<td>{{ form.name(size=32) }}</td>
<td>{{ form.paths() }}</td>
</tr>
</table>
{{ form.cbt_id() }}
<input type = "submit" value = "Add Path"/>
</form>
<script type="text/javascript">
$(document).ready(function() {
$('form').submit(function (e) {
var url = "{{ url_for('process_cbt') }}"; // send the form data here.
$.ajax({
type: "POST",
url: url,
dataType: "json",
data: $('form').serialize(),// serializes the form's elements.
success: function (data) {
console.log(data) // display the returned data in the console.
}
});
e.preventDefault(); // block the traditional submission of the form.
});
// Inject our CSRF token into our AJAX request.
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", "{{ form.csrf_token._value() }}")
}
}
})
});
</script>
{% endblock %}
My flask route:
#app.route('/process_cbt', methods = ['POST'])
#login_required
def process_cbt():
form = PathsForm()
if form.validate_on_submit():
return jsonify(data={'message':'Success'})
return jsonify(data={'message': 'Failure'})
It looks like the csrf token is being correctly implemented and I have followed the documentation to setup CSRF protection.
Can anyone tell me why I might be receiving an empty form?
Thank you
I believe you need to put headers in your AJAX request that include the CSRF token, like so
headers: {
"X-CSRFToken": "{{csrf_token()}}"
}
After way more googling than I'd like to admit I saw this answer and realized my problem was not so ajax based as it was a problem with the form validating. I put in my choices dynamically so when it was trying to validate it did not have any choices to validate on. I changed my view to the below and it all worked out.
def process_cbt():
form = PathsForm()
choice = request.form.get('paths')
form.paths.choices = [(choice,choice)]
if form.validate_on_submit():
return jsonify(data={'message':'success'})
return jsonify(data={'message': 'Failure'})

Django Ajax Modify avatar failed

I'm trying to use Django Ajax to Modify user's avatar, but it doesn't work.There is no any error information, just doesn't work.
Here is my form in template:
<form class="clearfix" id="jsAvatarForm" enctype="multipart/form-data" autocomplete="off" method="post" action="{% url 'users:image_upload' %}" target='frameFile'>
<img id="avatarShow" src="{{ MEDIA_URL }}{{ request.user.image }}"/>
<input type="file" name="image" id="avatarUp" class=""/>
<button type="submit">Modify Avatar</button>
{% csrf_token %}
</form>
Here is my Ajax:
$("#jsAvatarForm").submit(function(){
var image = $("#avatarShow").val()
$.ajax({
cache: false,
type: "POST",
url:"{% url 'users:image_upload' %}",
data:{'user_pk':{{ user.pk }}, 'image':image},
async: true,
beforeSend:function(xhr, settings){
xhr.setRequestHeader("X-CSRFToken", "{{ csrf_token }}");
},
success: function(data) {
if(data.status == 'fail'){
if(data.msg == '用户未登录'){
window.location.href="login";
}else{
alert(data.msg)
}
}else if(data.status == 'success'){
window.location.reload();//refresh current page.
}
},
});
return false;
});
Here is views.py:
class UploadImageView(LoginRequiredMixin, View):
def post(self, request):
user_pk = request.POST.get("user_pk", 0)
image = request.FILES.get('image')
user_change = UserProfile()
user_change.id = user_pk
user_change.image = image
user_change.save
return HttpResponse('ok')
Actually I also have a forms.py but I don't know how to use it with ajax:
class UploadImageForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ['image']
Here is my user model, note:I have rewrote my own USER:
class UserProfile(AbstractUser):
image = models.ImageField(upload_to="image/%Y/%m", default="image/default.png", max_length=100,verbose_name='头像')
class Meta:
verbose_name = "用户信息"
verbose_name_plural = verbose_name
def __str__(self):
return self.username
Any friend can help?Thank you so much!
Obviously You can send file such as image to django via js, depending of what you have posted in your question, I am showing you a way that you can proceed, there are lots of other ways to accomplish this fairly.
Feel free to comment, if anything is not working.
$("#jsAvatarForm").submit(function(){
var form = this;
// var image = $("#avatarShow").val() You can not retrieve the image like this
var formData = new FormData($(form)[0]);
// Everything inside the html form is serialized in the formData
// No need to add X-CSRFToken, {% csrf_token %} is inside the form
formData.append("NEW_KEY","NEW_VALUE"); # In case of further values
$.ajax({
url:$(form).attr('action'),
type:$(form).attr('method'),
data:formData,
processData: false,
contentType: false, //Don't set any content type header
success: function(){
// all your stuffs here
$('#avatarShow').attr('src',data.avatar_url)
},
error:function(){
alert("error");
}
});
return false;
});
Django
from django.http import JsonResponse
# codes here
user_change.image = image
user_change.save()
return JsonResponse({'avatar_url':user_change.image.url})
Note: Show the post view that rendering this template.

Django Ajax Form submit wrongly redirect to another page

When I use ajax to submit a comment form in Django,the page will redirect to a blank page shows me the success data:
{"status":"success", "msg":"添加成功"}
,but not stay in current page.I want the page stay in current page and show me the new comment.
Here is my update_comment view:
def update_comment(request, news_pk):
news = get_object_or_404(News, id=news_pk)
comment_form = CommentForm(request.POST or None)
if request.method == 'POST' and comment_form.is_valid():
if not request.user.is_authenticated:
return render(request, 'login.html', {})
comments = comment_form.cleaned_data.get("comment")
news_comment = NewsComments(user=request.user, comments=comments, news=news)
news_comment.save()
# return redirect(reverse('news:news_detail', kwargs={'news_pk': news.id}))
return HttpResponse('{"status":"success", "msg":"添加成功"}', content_type='application/json')
else:
return HttpResponse('{"status":"fail", "msg":"添加失败"}', content_type='application/json')
Here is my ajax:
$(document).on('submit', 'comment_form', function(e){
e.preventDefault();
$.ajax({
cache: false,
type: "POST",
url:"{% url 'operation:update_comment' news.id %}",
data:{'news_pk':{{ news.id }}, 'comments':comments},
async: true,
beforeSend:function(xhr, settings){
xhr.setRequestHeader("X-CSRFToken", "{{ csrf_token }}");
},
success: function(data) {
if(data.status == 'fail'){
if(data.msg == '用户未登录'){
window.location.href="login";
}else{
alert(data.msg)
}
}else if(data.status == 'success'){
window.location.reload();//refresh current page.
}
},
});
});
Here is my form:
<form id="comment_form" action="{% url 'operation:update_comment' news.id %}" method="POST" >
{% csrf_token %}
<textarea id="comment_textarea"name="comment"></textarea>
<input type="submit" value="Submit"> </input>
</form>
Finally I made it!Thanks Lord!Very excited!
I have Three major issues in my previous code.
First:Since the ajax will post the news_pk to the view update_comment,so I don't need add news_pk in this view's url and template(in the url of <form> tag and the url in the ajax),so I removed them,or the data will still pass through Form but not ajax.
Second:My binding is incorrect,I have the click handler on the form it should be a submit handler. If I was binding it to a button then I'd use click a handler.Ajax not work in Django post
But for this part I'm still a some confused,between the button summit way and form submit way.
The third issue is I mistaked 'comments' and 'comment'.'comment' is the name attribute of <textarea> ,through which forms.py gets the data.
comments is defined by ajax through var comments = $("#js-pl-textarea").val(), so in the view I need use comments = request.POST.get("comments", "") but not comment,that's the reason why 'post failed'.
Following is my code.
Here is the ajax:
$("#comment_form").submit(function(){
var comments = $("#js-pl-textarea").val()
$.ajax({
cache: false,
type: "POST",
url:"{% url 'operation:update_comment' %}",
data:{'news_pk':{{ news.pk }}, 'comments':comments},
async: true,
beforeSend:function(xhr, settings){
xhr.setRequestHeader("X-CSRFToken", "{{ csrf_token }}");
},
success: function(data) {
if(data.status == 'fail'){
if(data.msg == '用户未登录'){
window.location.href="login";
}else{
alert(data.msg)
}
}else if(data.status == 'success'){
window.location.reload();//refresh current page.
}
},
});
return false;
});
Here is my udate_comment view:
#login_required
def update_comment(request):
news_pk = request.POST.get("news_pk", 0)
comments = request.POST.get("comments", "")
if int(news_pk) > 0 and comments:
news_comments = NewsComments()
news = News.objects.get(id=int(news_pk))
news_comments.news = news
news_comments.comments = comments
news_comments.user = request.user
news_comments.save()
return HttpResponse('{"status":"success", "msg":"添加成功"}', content_type='application/json')
else:
return HttpResponse('{"status":"fail", "msg":"添加失败"}', content_type='application/json')
Here is my form in template:
<form id="comment_form" action="{% url 'operation:update_comment'%}" method="POST" >
{% csrf_token %}
<textarea id="js-pl-textarea"name="comment"></textarea>
<input type="submit" value="Submit"> </input>
</form>
I really appreciate everyone's reply!With your reply I figured out these issue step by step!
I have something similar in my project. Its a script to like a song. I'm just gonna put the relevant codes here.
The ajax script. I put this script in a separate file named like_script.html. I call it in a template using django template include
<script>
$('#like').click(function(){
$.ajax({
type: "POST",
url: "{% url 'song:like_song' %}",
data: {'pk': $(this).attr('pk'), 'csrfmiddlewaretoken': '{{ csrf_token }}'},
dataType: "json",
success: function(response) {
alert(response.message);
},
error: function(rs, e) {
alert(rs.responseText);
}
});
})
</script>
The django view
import json
from django.http import HttpResponse
from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_POST
#login_required
#require_POST
def song_like_view(request):
if request.method == 'POST':
user = SiteUser.objects.get(user=request.user)
pk = request.POST.get('pk', None)
song = get_object_or_404(Song, pk=pk)
if song.likes.filter(pk=user.pk).exists():
song.likes.remove(user)
song.like_count = song.likes.count()
song.save(update_fields=['like_count'])
message = "You unstarred this song.\n {} now has {} stars".format(song.title, song.like_count)
else:
song.likes.add(user)
song.like_count = song.likes.count()
song.save(update_fields=['like_count'])
message = "You starred this song.\n {} now has {} stars".format(song.title, song.like_count)
context = {'message' : message}
return HttpResponse(json.dumps(context), content_type='application/json')
The url
urlpatterns = path("like/", views.song_like_view, name='like_song'),
The template where the script is called
<a class="btn btn-sm btn-primary" href="" id="like" name="{{ song.pk }}" value="Like"></i> Unstar</a>
{% include 'like_script.html' %}
Same button for like and unlike. I hope you can follow the logic to make yours right. Notice that in your view you don't need to include the pk. Just get it from the POST data pk = request.POST.get('pk', None)

How to update Context processor variables using ajax request, without refreshing page

Currently am displaying notifications count on header for new notifications, they have is_read = False, I want to update is_read = True and remove notifications count, Here is what i am doing
context_processor
def notification(request):
"""Provide the count of unread notifications for an authenticated user."""
if request.user.is_authenticated():
notifications = Notification.objects.filter(user=request.user, is_read=False).\
order_by('-created')[:5]
return {'notification_count': len(notifications),
'notifications': notifications}
else:
return {}
html template and ajax call
{% if notification_count >= 1 %}
<a id="notification_menu" data-toggle="dropdown" class="dropdown-toggle" href="#">
<i class="ace-icon fa fa-bell icon-animated-bell"></i>
<span class="badge badge-important">{{ payzaat_notification_count }}</span>
</a>
{% endif %}
<script type="text/javascript">
(function($){
$('#notification_menu').on("click",function(){
console.log('clicked');
$.ajax({
type: "GET",
url: "{% url 'parent:update_notification' %}",
dataType: 'json',
success: function(response){
return true;
},error:function(response){
return false;
}
});
});
})(jQuery);
</script>
update view
class UpdateNotification(FormView):
#method_decorator(parent_required)
def dispatch(self, request, *args, **kwargs):
return super(UpdateNotification, self).dispatch(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
for record in Notification.objects.filter(user=self.request.user, is_read=False):
record.is_read = True
record.save()
return HttpResponse("OKAY")
My model got updated, but template still displaying count until i refresh my page
You have to clear the notification count on ajax success:
success: function(response) {
$("#notification_menu span").text("");
}
Thanks nima
As context processors run on page render so
I have done it like
success: function(response) {
$(this + ".badge .badge-important").html("");
}

Django. Display data with Ajax

I have a list of my all posts:
def posts_list(request):
posts = Post.objects.all()
return render_to_response('posts.html', {'posts':posts},context_instance=RequestContext(request))
in template:
{% for p in posts %}
{{ p.name }}
{% endfor %}
I want post after clicking on its name shows box(div) with all information about this post(Post model: name, content, date, author)
How to do it using jquery(ajax)?
Thank you for your interest.
You'll need to add an additional view:
new view:
def post_content(request, post_id):
post = get_object_or_404(Post, id = post_id)
return render_to_response('post_info.html', {'post':post},context_instance=RequestContext(request))
You'll need to create a post_info.html that gives more information about a post.
in your posts template:
{% for p in posts %}
<span data-url='{% url post_content post_id=p.id %}' class='post'>{{ p.name }}<span class='more_info'></span></span>
{% endfor %}
then you'd have the following Javascript (using Jquery in this example)
$(document).ready( function () {
$('.post').on('click', function() {
var span = $(this);
$.ajax({
url: span.attr('data-url')
}).done(function(data) {
span.find('.more_info').html(data);
});
});
});
This will replace the contents of the span with class more_info with the data from the server.
Edit: You'll also need to add something to your urls.py file, with name= "post_content" set.
template.html:
{% for p in posts %}
<div class="container"
data-name="{{ p.name }}"
data-content="{{ p.content }}">
{{ p.name }}
</div>
{% endfor %}
script.js:
$(document).ready(function(){
$('div.container').click(function(event){
var name = $(event.target).attr('data-name');
var content = $(event.target).attr('data-content');
$.ajax({
url: 'ajax/test/',
type: 'POST',
data: {
'name': name,
'content':content
},
success: function(data) {
alert('Load was performed.');
}
});
});
});
As you can see, I store model fields as element attributes (data-name="", ...). They can be retrieved with .attr() javascript method.
Then I bind click event for all divs with class="container". On click script retrieves previously stored data and make ajax-request. Ajax calls are described in documentation. Code have not been tested, so I can not be sure in it efficiency.
If I did not understand your goal correctly, let me know

Resources