Django, submiting a form via AJAX - ajax

I have seen answers (here and here) for similar questions, but none of them work in my case. I have a simple form in a template, I am using bootstrap for rendering.
Once I submit the form, the response is rendered directly in the browser. When I return to the previous page (with the browser's button) then the success part of the AJAX call is executed.
forms.py
class QueryForm(forms.Form):
query = forms.CharField(label='Discover something', max_length=256)
views.py
def query_view(request, id):
if request.method == 'POST':
# Just for testing, send True
response_data = {
'result': True
}
return HttpResponse(json.dumps(response_data), content_type="application/json")
else:
try:
# Create a form and send it to the template
query_form = QueryForm()
return render(request, 'query_template.html', {'query_form': query_form})
except ObjectDoesNotExist:
return render(request, 'error.html')
urls.py
urlpatterns = [
url(r'^query', views.query_view, name='query_view'),
url(r'^', views.home, name='home'),
]
query_template.html
{% extends 'base.html' %}
{% load static %}
{% load bootstrap3 %}
{% block content %}
{# Display a form #}
<form method="post" class="form">
{% csrf_token %}
{% bootstrap_form query_form %}
{% buttons %}
<button class="btn btn-primary" id="query-button">
{% bootstrap_icon "star" %} Submit
</button>
{% endbuttons %}
</form>
<ul id="result"></ul>
<script src="{% static 'scripts/main.js' %}"></script>
{% endblock %}
main.js
$('#query-button').click(function (event) {
$.ajax({
url: "/query/",
type: "POST",
data: {},
cache: false,
// handle a successful response
success: function (json) {
console.log(json); // log the returned json to the console
$("#result").html("<li>" + json.result + "</li>");
console.log("success"); // another sanity check
},
// handle a non-successful response
error: function (xhr, errmsg, err) {
console.log(xhr.status + ": " + xhr.responseText);
}
});
});
// It also includes functions to manage the CRFS token
I have been playing with the code. If instead a form I use <input> and <button id='query-button'> it renders the response without reloading the page.

You need to prevent the default submit action of the HTML form, via event.preventDefault().

You can using FormData instead of custom send data, open below links:
How to send FormData objects with Ajax-requests in jQuery?
https://developer.mozilla.org/en-US/docs/Web/API/FormData

Related

Flask, WTForms - send the whole form using ajax

I am wondering how to send the whole wtform using ajax request (this one does not work):
function addReport(e){
e.preventDefault();
var serializeData = $('form').serialize();
$.ajax({
type:'GET',
url:'/reports_list',
data: serializeData,
success:function(result){
},
async: false
});
}
I have one main form, which includes another:
Reports = FieldList(FormField(ReportsForm))
The idea is to render the form under form, in case user wants to add another one. In short, it should just copy this underform and clone it, then renders to the main one.
How can I pass the entire form in ajax request?
It seems to me that you are trying to nest form elements. So you only send the innermost form. The validation errors are also not displayed when the form is sent via AJAX.
I think you can follow your approach more easily if you add the necessary form fields by cloning with JavaScript without submit.
The following example breaks the nested forms into fieldsets.
If a fieldset is to be added, the previous one is cloned and the necessary attributes are adapted to the requirements.
When removing, the last one is removed.
from flask import (
Flask,
render_template,
request
)
from flask_wtf import FlaskForm
from wtforms import FieldList, FormField, StringField, SelectField
from wtforms.validators import DataRequired
app = Flask(__name__)
app.secret_key = 'your secret here'
class ReportForm(FlaskForm):
class Meta:
csrf = False
title = StringField('Name', [DataRequired()])
lang = SelectField('Language', choices=[('cpp', 'C++'), ('py', 'Python'), ('text', 'Plain Text')])
class ReportsForm(FlaskForm):
reports = FieldList(FormField(ReportForm), min_entries=1)
#app.route('/reports', methods=['GET', 'POST'])
def reports():
form = ReportsForm(request.form)
if form.validate_on_submit():
for report in form.reports.data:
print(report)
return render_template('reports.html', **locals())
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Reports</title>
</head>
<body>
<form name="my-form" method="post">
{{ form.csrf_token }}
{% for subform in form.reports -%}
<fieldset name="subform">
{% for field in subform -%}
<div>
{{ field.label() }}
{{ field() }}
{% if field.errors -%}
<ul class="errors">
{% for error in field.errors -%}
<li>{{ error }}</li>
{% endfor -%}
</ul>
{% endif -%}
</div>
{% endfor -%}
</fieldset>
{% endfor -%}
<button id="btn-less" type="button">Less</button>
<button id="btn-more" type="button">More</button>
<button type="submit">Submit</button>
</form>
<script type="text/javascript">
(function() {
const btnMore = document.querySelector('#btn-more');
btnMore.addEventListener('click', function() {
// Clone the previous fieldset element.
const subform = document.querySelector('fieldset[name="subform"]:last-of-type');
const newform = subform.cloneNode(true);
// Remove possible validation error messages.
const errors = newform.querySelector('.errors');
errors && errors.remove();
// Update the necessary attributes.
const fields = newform.querySelectorAll('input, select');
fields.forEach(field => {
const attrName = field.name;
field.name = field.id = attrName.replace(/^(.*)-([0-9]+)-(\w+)$/g, (...args) => {
return [args[1], Number(args[2]) + 1, args[3]].join('-');
});
field.value = '';
const labels = newForm.querySelectorAll(`label[for="${attrName}"]`);
labels.forEach(label => label.setAttribute('for', field.id));
});
// Add the new fieldset.
subform.after(newform);
});
const btnLess = document.querySelector('#btn-less');
btnLess.addEventListener('click', function() {
const subforms = document.querySelectorAll('fieldset[name="subform"]');
if (subforms.length > 1) {
// Remove the last of the fieldset elements.
subforms[subforms.length - 1].remove();
}
});
})();
</script>
</body>
</html>

csrf token for ajax in django2

I am learning Django2,and try to make a login page with csrf_token and ajax.
I hope that if user hasn't lgoin,that will turn to the login page and send a variable next as a tag of the page before login.If user login successfully that I can turn to the homepage or page marked by next.
I read the docs of Django2, and try to code like below,however,when I click "LOGIN" button,it just refresh the login page and get no error
I am confused and have no idea already.Please help.
login views:
def login(request):
if request.is_ajax():
uf = UserForm(request.POST)
if uf.is_valid():
# get info from form
username = uf.cleaned_data['username']
password = uf.cleaned_data['password']
user = auth.authenticate(request, username=username, password=password)
if user is not None: # user match
auth.login(request, user)
if request.GET.get('next'):
next_url = request.GET.get('next')
return JsonResponse({'redirect_url': next_url})
# return redirect(request.GET.get('next'))
else:
return JsonResponse({'redirect_url': 'home'})
else: # user not match
error_msg = ["username or pwd mistake"]
return JsonResponse({'error_msg': error_msg})
else:
uf = UserForm()
return render(request, 'login.html', {'uf': uf})
html :
<form>
{% csrf_token %}
{{ uf.username }}
{{ uf.password }}
<div id="errorMsg"></div>
<button type="submit" class="btn btn-default" id="loginButton">login</button>
<input type="hidden" name="next" id="redirect-next" value="{{ next|escape }}"/>
</form>
JQuery:
$("#loginButton").click(function () {
$.ajax({
url: "",
type: 'POST',
dataType: "json",
data: {username: $("#inputEmail3").val(), password: $("#inputPassword3").val()},
beforeSend: function (xhr, settings) {
var csrftoken = Cookies.get('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
},
success: function (result) {
if (result.error_msg) {
$('#errorMsg').show().text('user info error') //print an alert on the page
}
else {
location.href = result.redirect_url //turn to homepage or page before login
}
}
})
});
You don't need to take effort to write a login view of your own like this. Django provides easier methods to implement it.
First make sure the following are included in your settings.py
MIDDLEWARE_CLASSES = [
...
'django.contrib.auth.middleware.AuthenticationMiddleware',
...
]
INSTALLED_APPS = [
...
'django.contrib.auth',
'django.contrib.contenttypes',
...
]
Add all the login URLs to your main urls.py:
from django.urls import path
from django.conf.urls import include
urlpatterns = [
....
path('accounts/', include('django.contrib.auth.urls')),
....
]
Don't forget to run python manage.py migrate to create the tables required for the auth app. Now that the app and URLs are ready, templates need to be created. All the templates for the app should be placed under a folder named registration under your templates directory. The directory structure should be something like.
your_django_app/
templates/
registration/
login.html
__init__.py
apps.py
settings.py
urls.py
views.py
wsgi.py
The contents of the login.html should be something like:
<form id="loginform" action="{% url 'login' %}" method="POST">
{% csrf_token %}
{% if next %}
<input type="hidden" name="next" value="{{ next }}" />
{% endif %}
<input name="username" id="id_username" type="text">
<label>Username</label>
<input name="password" id="id_password" type="password">
<label>Password</label>
{% if form.errors %}
Error! Wrong credentials.
{% endif %}
<button type="submit">Login</button>
</form>
After this include these in your settings.py file for redirecting users correctly after login.
LOGIN_REDIRECT_URL = '/'
LOGIN_URL = '/accounts/login'
You are all set to go. Make sure to create at least one user before trying this out by running python manage.py createsuperuser. For all pages that require users to login before viewing them you can use the #login_required decorator above their respective view functions to redirect them to the login page before showing the page. Example:
from django.shortcuts import render
from django.contrib.auth.decorators import login_required
#login_required
def home(request):
return render(request, 'home/index.html')
Here there is a short and simple way to parse the csrf_token using ajax:
Inside the script tag.
$.ajax({
url: window.location.pathname,
type: 'POST',
data: {
......,
// Note this step.
'csrfmiddlewaretoken': "{{ csrf_token }}"
},
success: function() {
.....
}
});
Hope things work well as I have used this thing to parse the csrf_token in my 2 Django based projects. Cheers!
This might be related to this issue
As your button tries to submit the form but you want it to be handled handled by the script.
try changing the button type to
type="button"

Can't get all comments to reload without page refresh after comment submitted thourgh ajax

I have been trying to come up with a scheme where the comments sections refreshes in a template when a user posts comments with the comment that was just posted being included. The page must not be refreshed.
Sorry for indentation.
template -
{% extends "home/header.html" %}
{% block content %}
{% if request.user.is_authenticated %}
<form>
{% csrf_token %}
<p>Comment: </p><input type="text" name="fname" id="posted_comment">
<input type="hidden" class='meme_char_id' meme_char_id={{meme.meme_char_id}}>
<input type="submit" id ="btnSubmit" name="submit" value="Post">
</form>
<!-- displaying comments here -->
<div class="box" id="comments_div">
<h3 class="h4">{{comment_count}} comments </h4>
<ul class="list-group">
{% for comment in all_comments %}
<h5>{{ comment.commentby.username }} : </h5>
<li class="list-group-item list-group-item-success">{{ comment.comment_text }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% endblock %}
Now, my JS file is -
$(document).ready(function() {
$('#btnSubmit').click(function(e) {
e.preventDefault();
var data = new FormData();
var comment = $('#posted_comment').val()
var meme_char_id = $(".meme_char_id").attr('meme_char_id')
data.append('comment', comment);
data.append('meme_char_id', meme_char_id);
$.ajax({
type: 'POST',
url: '<mydomain>/comment/',
data: data,
processData: false,
contentType: false,
success: function(reply) {
$(".comments_div").html(reply)
}
})
});
});
And finally my view is -
def comment(request):
if request.is_ajax():
comment = request.POST['comment']
meme_char_id = request.POST['meme_char_id']
this_meme = Memes.objects.get(meme_char_id=meme_char_id)
print "comment - ", comment, " meme_char_id - ", meme_char_id
new_comment = Comments(comment_text=comment, meme_id=this_meme.meme_id, commentby_id=request.user.id)
new_comment.save()
all_comments = Comments.objects.filter(meme_id=this_meme.meme_id).order_by('-created_at').values()
return HttpResponse(all_comments, content_type='application/json')
else:
raise Http404
Couple questions -
I want to return an Comments query set through ajax(don't see a point in jsonifying it, if I can just send the query set to the template.)
The queryset I am returning to template isn't picked up by the comments_div in the view.
I am a rookie so please explain step by step what I am doing wrong. Thanks.
You are sending back a JSON in a response and doing$(".comments_div").html(reply). You must be sending back a template rendered with all_comments as response on successful comment. Also, a queryset is not JSON serializable as far as I understand.

Render template into Symfony2 with ajax

I have a action in my controller for the index route
routing.yml
index:
pattern: /index
defaults: { _controller:AcmeDemoBundle:Default:index }
Controller for this path
public function indexAction()
{
return $this->render('AcmeDemoBundle:Plugin:index.html.twig');
}
And the index.html.twig template
{% extends'::base.html.twig' %}
{% block stylesheets %}
{% stylesheets filter='cssrewrite' output='css/*.css'
'bundles/acmedemo/css/*' %}
<link href="{{ asset_url }}" type="text/css" rel="stylesheet" />
{% endstylesheets %}
{% endblock stylesheets %}
{% block body %}
<br>
<div class="container">
<div class="wp_attachment_holder">
<div class="imgedit-response" id="imgedit-response-8"></div>
<div class="wp_attachment_image" id="media-head-8">
<p id="thumbnail-head-8"><img class="thumbnail" src="http://localhost/wordpress/wp-content/uploads/2014/06/121-1024x583.jpeg" style="max-width:100%" alt=""></p>
<p><a class="btn btn-sm btn-default" id="edik-wp-extended-edit">Редактировать</a> <span class="spinner"></span></p>
</div>
<div style="display:none" class="image-editor" id="image-editor-8">
</div>
</div>
<div id="output"></div>
<img class="thumbnail" data-attach-id="8" data-src="http://localhost/wordpress/wp-content/uploads/2014/06/121-1024x583.jpeg" style="max-width:100%" alt="">
<script>
$('#edik-wp-extended-edit').click(function() {
window.location= Routing.generate('ajax');
// $('#output').load('/ajax/index');
});
</script>
</div>
{% endblock %}`
When the button Редактировать is clicked i want to load another template with ajax.
another.html.twig
<div>Hello</div>
routing.yml
ajax:
pattern: /ajax/index
defaults: { _controller :AcmeDemoBundle:Default:ajax }
options:
expose: true
Controller for this path
public function ajaxAction()
{
$template = $this->renderView('AcmeDemoBundle:Plugin:another.html.twig');
return new Response($template);
}
But when i click the button my uri will be /ajax/index. What i want is that it stays by /index and the template will be rendered into my index template
What am i doing wrong?
Thanks.
First, your ajaxAction() should be a bit different as far as I know.
For me this works:
$template = $this->forward('AcmeDemoBundle:Plugin:another.html.twig')->getContent();
$json = json_encode($template);
$response = new Response($json, 200);
$response->headers->set('Content-Type', 'application/json');
return $response;
The forward() function renders the template and returns the rendered HTML code.
Your JavaScript file should look like this:
$.ajax({
type: "POST",
dataType: 'json',
url: Routing.generate('ajax'),
async: false //you won't need that if nothing in your following code is dependend of the result
})
.done(function(response){
template = response;
$('#your_div').html(template.html); //Change the html of the div with the id = "your_div"
})
.fail(function(jqXHR, textStatus, errorThrown){
alert('Error : ' + errorThrown);
});
You make an AJAX call to the your ajaxAction, which will return the HTML of the template you want to be rendered.
After that you just need to add a <div id="your_div"></div> at the position you want the template to be rendered. This workes perfectly for me.
To mention is that you need to break down the ajax template to just the code that should be shown.
Please try generate ajax route like this
window.location= '{{ path("ajax") }}';
Added:
For make ajax request change windows.location to ajax request
$( "#output" ).load( '{{ path("ajax") }}', function() {
alert('Load done');
});
Added explanation of use:
js code will work only if you put it on Twig template. If you put it to js file it will not work.
In case of original questions.
<div id="output"></div>
<img class="thumbnail" data-attach-id="8" data-src="http://localhost/wordpress/wp-content/uploads/2014/06/121-1024x583.jpeg" style="max-width:100%" alt="">
<script>
$('#edik-wp-extended-edit').click(function() {
$( "#output" ).load('{{ path("ajax") }}');
});
</script>
You can use something like FOSJsRoutingBundle to proper use SF2 routing in js

how to do ajax or jquery with django1.3

I am absolutely new to django framework and new to web development and python. Currently I'm trying to implement ajax in my project. I am not able to find any working sample. I need help with putting ajax or jquery in a django1.3 project.
Now I'm trying to implement ajax using dojango' app. I install it successfully and trying this tutorial.
urls.py:
(r'^dojango/', include('dojango.urls')),
(r'^simple/', 'views.simple'),
(r'^simple-ajax-set/', 'views.simple_ajax_set'),
views.py:
from django.shortcuts import render_to_response
from dojango.decorators import json_response
#expect_post_request
#json_response
def simple_ajax_set(request):
ret = {}
firstname = request.POST['firstname']
surname = request.POST['surname']
if len(surname)<3:
ret['error'] = 'Surname is too short.'
ret['success'] = False
if ret['success']:
# Store the data here
pass
return ret
simple.html
{% extends "dojango/base.html" %}
{% block dojango_page_title %}Simple AJAX with dojango{% endblock %}
{% block dojango_header_extra %}
<script type="text/javascript">
function userFormSubmit(){
var form = dojo.byId("userForm");
dojo.xhrPost({url:form.action,
handleAs:"json",
content:{surname:form.surname.value,
firstname:form.firstname.value
},
load:function(response, ioArgs){
if (response.success){
dojo.byId("info").innerHTML = "Submitted successfully";
} else {
dojo.byId("info").innerHTML = "Error: "+response.error;
}
},
error:function(data){ // This happens on a 500 error or alikes.
dojo.byId("info").innerHTML = "Error sending data.";
}
});
}
</script>
{% endblock %}
{% block dojango_content %}
<form id="userForm" onsubmit="userFormSubmit(); return false;" action="/simple-ajax-set/">
First name: <input id="firstname" /><br />
Surname: <input id="surname" /><br />
<input type="submit" value="Submit" /> <span id="info"></span>
</form>
{% endblock %}
When I run I got the exception
NameError at /simple-ajax-set/
name 'expect_post_request' is not defined
So I change my code as in the first part of the above tutorial, and I got the following exception
'MultiValueDictKeyError at /simple-ajax-set/' , "Key 'firstname' not found in <QueryDict: {}>" when click on the submit button at simple.html.
Please help me to find out the problem sand also share some links or codes containg ajax/jquery which works with django1.3. and python 2.7,
Can any one suggest working a hello world ajax or jquery example for django1.3?
"Pure" jQuery and django here. That made me happy when i was on your place. Good luck!

Resources