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
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'})
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.
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)
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("");
}
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