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
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'})
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.
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)
In Django, how to click button on page without passing any value to view.py and then reloading current page? I have a button in the HTML:
<button type="button" class="btn btn-primary" id="refresh">refresh page</button>
I want to call a view in view.py, in which new HTML will be returned:
#csrf_exempt
def topology_show_refresh(request):
'''topology refresh'''
plan = {...}
...
return render(request, 'topology_show.html', {'plan': plan})
The plan dictionary will be used in the new page.
{% if plan.try == 'first' %}
<script type="text/javascript">
var max_lane_num = {{plan.max_lane_num}};
var flag = 0;
</script>
{% else %}
<script type="text/javascript">
var max_lane_num = {{plan.lane_second}};
var flag = 1;
</script>
{% endif %}
In my way, I use ajax to jump to this view, but I have no idea how to handle the return, e.g., pass the plan to HTML.
$(function(){
$("#refresh").click(function(event){
url = 'refresh/';
$.post(url, {}, function(ret){
//do something
//how to pass "plan" dictionary to HTML
});
});
});
You are very close to the task of reloading page,
Use 'location.reload(true)' instead of 'window.location.reload();'
And handle the response data by success() function.
Try this :
$(function(){
$("#refresh").click(function(event){
$.ajax({
type: "POST",
url: 'refresh/',
success:function(response) {
location.reload(true);
//do something with 'response'
}
});
});
I'm using two datepickers from the jQuery library for start and end dates. Once I have the two dates I send them to my django view via ajax. I then run the model query in my views which will return a filtered dict into my django context. My question now is how can I reload the table on the template?
Would I have to define a Javascript function to load a table and call it on ajax success? Or perhaps a better way?
html
<table class="table table-bordered">
<tr>
<th>Start Date: <input id="mail_start_date"/></th>
<th>End Date: <input id="mail_end_date"/></th>
<th><button id='btn'>Filter</button></th>
</tr>
<tr>
<th>Desk</th>
<th># of Packages</th>
</tr>
{% for desk, count in pick_dict.items %}
<tr>
<td>{{desk}}</td>
<td>{{count}}</td>
</tr>
{% endfor %}
Javascript
$(document).ready(function(){
$('#mail_start_date').datepicker({ dateFormat: "yy-mm-dd" });
$('#mail_end_date').datepicker({ dateFormat: "yy-mm-dd" });
$('#btn').click(function(){
var start = $('#mail_start_date').val();
var end = $('#mail_end_date').val();
$.ajax({
url: "/apps/centraldesk/mail/stats/",
type: "POST",
data: {
'start': start,
'end': end,
csrfmiddlewaretoken: '{{ csrf_token }}',
},
success: "SOMTHING NEEDS TO HAPPEN HRERE"
});
});
views.py
def mail_stats(request):
pick_dict = {}
if request.is_ajax():
pick_start = request.POST['start']
pick_end = request.POST['end']
pick_start = str(pick_start)
pick_end = str(pick_end)
in_date = datetime.datetime.strptime(pick_start, "%Y-%I-%d")
out_date = datetime.datetime.strptime(pick_end, "%Y-%I-%d")
pick_list = MailRecord.objects.filter(timeIn__range=(pick_start, pick_end))
for item in pick_list:
if pick_dict.has_key(item.desk.name):
pick_dict[item.desk.name] += 1
else:
pick_dict[item.desk.name] = 1
context = {
"mail_list" : mail_list,
"pick_dict" : pick_dict,
}
return render("mail_stats.html", context, context_instance=RequestContext(request, processors=[custom_proc]))
Yes, you define a javascript function, like so
...
},
success: function(json) {
// Access your table here and manipulate it with the returned data from "json" (json is the dictionary given by your django view)
}
});
...
You could also add an error-function next to the success function.