thanks in advance. I know this has been asked a few times. But after reading the previous questions, reading and understanding JSON and AJAX forms tutorials, I still can't find a way to not having the website refreshed after submitting a form. I would really appreciate it if any of you with a higher knowledge of JavaScript is able to give a hand.
This is a newsletter at the bottom part of the Home page that just asks for a name and an email and it keeps the information in the database, it works perfect and I just would like to reply with a confirmation message without refreshing, because it replies a message but the user has to go to the bottom of the page again to see it which is not practical at all.
The HTML is
<div id="contact_form">
<div class="Newsletter">
<form id="form" enctype="multipart/form-data" method="POST" action="" style="text-align: left;">
{% csrf_token %}
<div class="fields">
<div class="fields">
<label for="name" id="name_label">Name</label>
<input type="text" name="name" minlength="3" placeholder="e.g. John Smith" id="name" required>
</div>
<div class="fields">
<label for="email" id="email_label">Email</label>
<input type="email" name="email" placeholder="e.g. john#example.com" id="email" required>
</div>
</div>
<div class="submit">
<button type='submit' id="submit" >Subscribe</button>
</div>
{% include 'messages.html' %}
</form>
</div>
</div>
The index view
def index(request):
"""View function for home page of site."""
if request.method == 'POST':
form = NewsUserForm(request.POST)
if form.is_valid():
instance = form.save(commit=False) #we do not want to save just yet
if NewsUsers.objects.filter(email=instance.email).exists():
messages.warning(request, 'Your email already exists in the newsletter database')
else:
instance.save()
messages.success(request, 'Great! Your email has been submitted to our database.')
try:
send_mail('Welcome ', 'Thank you for subscribing to the Newsletter. ', 'user123#gmail.com',[instance.email], fail_silently=False)
except BadHeaderError: #add this
return HttpResponse('Invalid header found.') #add this
form = NewsUserForm()
return render(request, 'index.html', {'form':form})
Most of the tutorials suggest to create another view + an url for that view + ajax code
I tried this one from here (https://pytutorial.com/how-to-submit-a-form-with-django-and-ajax#top) without success, also eliminating "post" in html method but still not working, even the info got from the form appears in the url. Any help will be welcome, thank you very much.
jquery unsuccessful code
$('#form').on('submit', function(e){
e.preventDefault();
$.ajax({
type : "POST",
url: "{% url 'index' %}",
data: {
name : $('name').val(),
email : $('email').val(),
csrfmiddlewaretoken: '{{ csrf_token }}',
dataType: "json",
},
success: function(data){
$('#output').html(data.msg) /* response message */
},
failure: function() {
}
});
});
unsuccessful ajax view function and url
def ajax_posting(request):
if request.is_ajax():
name = request.POST.get('name', None) # getting data from first_name input
email = request.POST.get('email', None) # getting data from last_name input
if name and email: #cheking if first_name and last_name have value
response = {
'msg':'Your form has been submitted successfully' # response message
}
return JsonResponse(response) # return response as JSON
#path('ajax-posting/', views.ajax_posting, name='index'),# ajax-posting / name = that we will use in ajax url
Related
I have a Django project with a form in an HTML file, and I'd like to update the text on the submit button of that form WITHOUT a page reload. Essentially:
I click submit on the form
Python handles the submit with the form data
The button text is updated to say "show result"
If I understand correctly, I have to use AJAX for this. The problem is that the form submit relies on an API call in Python, so the HTML essentially has to always be "listening" for new data broadcasted by the views.py file.
Here's the code I have (which doesn't work, since when I hit submit I'm greeted by a page with the JSON response data and nothing else):
views.py:
def home(request):
if request.method == "POST":
print("Got form type", request.content_type)
return JsonResponse({"text": "show result"})
return render(request, 'home.html')
home.html:
<div class="content" onload="document.genform.reset()">
<form name="genform" autocomplete="off" class="form" method="POST" action="" enctype="multipart/form-data">
{% csrf_token %}
<div class="title-sect">
<h1>AJAX Test Form</h1>
</div>
<div class="submit">
<button id="submit" type="submit">Submit</button>
</div>
</form>
</div>
<script type="text/javascript">
function queryData() {
$.ajax({
url: "/",
type: "POST",
data: {
name: "text",
'csrfmiddlewaretoken': '{{ csrf_token }}',
},
success: function(data) {
var text = data['text'];
var button = document.getElementById('submit');
button.innerHTML = text;
setTimeout(function(){queryData();}, 1000);
}
});
}
$document.ready(function() {
queryData();
});
</script>
I've imported jQuery with the script <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-1.9.0.min.js"></script>. Any idea why this doesn't work in its current state? Thanks!
I am working on a django application. This application hold two forms. In the first form (image_form) the user can upload an image. In the second form (image_description) the user can fill some description about the image. when the image is uploaded in the first form, an image classifier runs on the image tries to fill parts of the image_description form. Once the second form is submitted the item is displayed in a new page (item_list). This is the urlpatterns for the application.
urls.py
urlpatterns = [
path('', views.index, name='home'),
path('accounts/', include('django.contrib.auth.urls')),
path('signup/', views.signup, name='signup'),
path('items/', views.item_list, name='item_list'),
path('items/upload/description/', views.upload_item, name='upload_item'),
path('items/<int:pk>/', views.delete_item, name='delete_item'),
path('items/upload/image_classification/', views.image_classification, name='image_classification'),
]
here item_list is the page where all items are displayed. This page is displayed once the image_description form is submitted.
upload_item page contains both the forms.
image_classification runs when upload image button in the first form is clicked. This happens in the upload_item page.
views.py
def item_list(request):
items = Item.objects.all()
return render(request, 'item_list.html', {'items':items})
def upload_item(request):
if request.method == 'POST':
form_des = ItemForm(request.POST, request.FILES)
if form_des.is_valid():
form_des.save()
return redirect('item_list')
else:
form_des = ItemForm()
return render(request, 'upload_item.html', {'form_des': form_des})
def image_classification(request):
form_des = ItemForm()
if request.method == 'POST':
if 'file' in request.FILES:
handle_uploaded_file(request.FILES['file'], str(request.FILES['file']))
img = np.expand_dims(cv2.resize(cv2.imread(os.path.join('./media/item/img/', str(request.FILES['file']))), (170, 100)), axis=0)
cat_prediction = cat_classifier.predict_classes(img)[0]
pattern_prediction = pat_classifier.predict_classes(img)[0]
form_des.fields['title'].widget.attrs['value'] = cat_prediction
form_des.fields['pattern'].widget.attrs['value'] = pattern_prediction
form_des.fields['user'].widget.attrs['value'] = request.user
form_des.fields['user'].widget.attrs['readonly'] = True
return render(request, 'upload_item.html', {'form_des': form_des})
else:
return redirect('upload_item')
upload_item.html template
<div class="container">
<div class="row justify-content-center">
<div class="col-7">
<center>
<h2>Upload image</h2>
<!-- <div class="row justify-content-center"> -->
<div class="upload-btn-wrapper">
<form action="{% url 'image_classification' %}" method="POST" enctype="multipart/form-data" data-url="image_classification/" class="my_form">
{% csrf_token %}
<input type="file" name="file" id="file" class="inputfile" multiple/>
<label for="file" class="btn btn-outline-dark btn-lg mt-5 select">Choose a file</label>
<input class='btn btn-primary btn-lg btn-block upload_image_button' type="submit" value="Upload image" disabled/>
</form>
</div>
<!-- </div> -->
<center>
<p class='font-weight-bold mt-5 mb-5 text-danger'>Step: 1 of 2</p>
</center>
<div class="separation"></div>
<h2>Item description</h2>
</center>
<div class="card mb-2 mt-3">
<div class="card-body">
<form method="post" enctype="multipart/form-data" action="{% url 'upload_item' %}" id='item_des'>
{% csrf_token %}
{{form_des.title|as_crispy_field}}
{{form_des.pattern|as_crispy_field}}
{{form_des.color|as_crispy_field}}
{{form_des.user|as_crispy_field}}
<button type="submit" class='btn btn-primary btn-lg btn-block save_btn'>Save item</button>
</form>
</div>
</div>
<center>
<p class='font-weight-bold mt-2 mb-5 text-danger'>Step: 2 of 2</p>
</center>
</div>
</div>
</div>
My problem is when the upload image button is clicked in the first form the url changes from items/upload/description/ to items/upload/image_classification/ and the page reloads with the auto filled sections in the second form.
I want to use AJAX to auto-fill the second form without reloading the page but am not sure how to do it. I followed a few tutorials but am not able to achieve this.
Please help me
Thank you
[EDIT1]
Based on Adeel Siddiqui's answer, I made a few changes to views.py
views.py
def image_classification(request):
form_des = ItemForm()
user =str(request.user)
if request.method == 'POST':
if 'file' in request.FILES:
handle_uploaded_file(request.FILES['file'], str(request.FILES['file']))
img = np.expand_dims(cv2.resize(cv2.imread(os.path.join('./media/item/img/', str(request.FILES['file']))), (170, 100)), axis=0)
cat_prediction = cat_classifier.predict_classes(img)[0]
pattern_prediction = pat_classifier.predict_classes(img)[0]
form_des.fields['title'].widget.attrs['value'] = cat_prediction
form_des.fields['pattern'].widget.attrs['value'] = pattern_prediction
form_des.fields['user'].widget.attrs['value'] = request.user
form_des.fields['user'].widget.attrs['readonly'] = True
context = {
'tops_prediction' :tops_prediction,
'pattern_prediction':pattern_prediction,
'user' :user,
}
return HttpResponse(json.dumps(context))
else:
return redirect('upload_item')
How do I access this context from fillImageDescriptionText in jquery and auto-fill the second form in the upload_item page?
[EDIT-2] screenshot of the output after using the updated answer
You could prevent the default event happening when the form is submitted. Suppose your image classification form has the id image-classify-form, then:
$("#image-classify-form").submit(function(event) {
event.preventDefault();
imageClassifyAjax();
}
where your imageClassifyAjax function does this (i previously forgot to create the FormData object on line 2 which was causing the POST request to fail):
function imageClassifyAjax() {
let $form = $("#image-classify-form");
let form_data = new FormData($form[0]);
$.ajax({
url: $form.attr('action'),
type: $form.attr('method'),
data: form_data,
processData: false,
contentType: false,
dataType: 'json',
success: function (data) {
fillImageDescriptionText(data);
},
error: function (xhr) {
console.log("Something went wrong: " + xhr.responseText);
}
});
}
and fillImageDescriptionText uses the json data returned by the view to fill up the form for the image description. So you have to modify what your view does on the POST method. You have to return an HttpResponse instead of doing a render template:
import json
from django.http import HttpResponse
def image_classification(request):
form_des = ItemForm()
if request.method == 'POST':
...
...
return HttpResponse(json.dumps({'title': cat_prediction, 'pattern': pattern_prediction, 'user': request.user.email}))
fillImageDescriptionText receives this json object as input, so you can basically:
function fillImageDescriptionText(data) {
$("#item_des #id_title").val(data.title);
$("#item_des #id_pattern").val(data.pattern);
$("#item_des #id_user").val(data.user);
$("#item_des #id_user").prop('readonly', true);
}
where id_ is the id generated for the elements by django crispy forms or whatever package you are using for the forms. It is mostly prefixed with an "id_". You can check it up by inspecting elements from the browser.
I am working with Django and AJAX,
I have a template where people can select an option and then click a submit button. The button fires an Ajax function that sends the data to my view where it is processed and should return a value back to the template.
The issue is when the post goes through, it hits the view, and nothing is returned to the template, I am not sure if this is because the view isn't getting any data, but it isn't firing any of my conditional statements, so it acts like its working but doesn't return anything.
My HTML form:
<form method="POST" id="buy_form" name="buy_form" action="{% url 'manage:buy' %}">
{% csrf_token %}
<div class="buy_top_section">
<div class="width">
<div class="spacing">
<h3 class="sell_title">How much do you want to sell?</h3>
<input type="text" id="amount" class="buy_input_top" maxlength="10" name="amount" type="number" required>
<select id="selected" class="buy_selection" name="wanted">
<option value="generate_b">BTC</option>
<option value="generate_e">ETH</option>
<option value="generate_l">LTC</option>
</select>
</div>
<span class="float_clear"></span>
<button id='generate' type="submit" value="currency_details" class="custom_button"l">Generate
</button>
</div>
</div>
</form>
<!-- What needs to be returned from thew view -->
<h1>{{ address }}</h1>
My AJAX
$(document).on('submit', '#buy_form', function (e) {
e.preventDefault()
$.ajax({
type: 'POST',
url:'/manage/buy/',
data:{
currency:$('selected').val(),
amount:$('#amount').val(),
csrfmiddlewaretoken:$('input[name=csrfmiddlewaretoken]').val()
},
success: function (){
}
})
});
My Django View
def buy_corsair(request):
if request.method == 'POST':
if request.POST.get('wanted') == 'generate_b':
# Get the amount entered
amount = request.POST.get('amount')
# Generate a new B address
new_b_address = client.create_address(b_account_id)['address']
# Point the address at the user
request.user.user_address.b_address = new_b_address
# Save address to current user
request.user.user_address.save()
# Pass the address to the template
context = {'address': new_b_address}
return render(request, context)
urls.py
urlpatterns = [
# Buy Page
path('buy/', views.buy_corsair, name='buy'),
]
Ajax requests run in background, django render function render a template to body, so you can not render this way. You could like this;
dont forget include
from django.http import HttpResponse
def buy_corsair(request):
if request.method == 'POST':
if request.POST.get('wanted') == 'generate_b':
# Get the amount entered
amount = request.POST.get('amount')
# Generate a new B address
new_b_address = client.create_address(b_account_id)['address']
# Point the address at the user
request.user.user_address.b_address = new_b_address
# Save address to current user
request.user.user_address.save()
# Pass the address to the template
return HttpResponse(new_b_address)
in your js;
$.ajax({
type: 'POST',
url:'/manage/buy/',
data:{
currency:$('selected').val(),
amount:$('#amount').val(),
'csrfmiddlewaretoken': "{{ csrf_token }}"
},
success: function (data){
$('h1').html(data);
}
})
});
in your Django view
import json
from django.http import HttpResponse
def buy_corsair(request):
if request.method == 'POST':
if request.POST.get('wanted') == 'generate_b':
# Get the amount entered
amount = request.POST.get('amount')
# Generate a new B address
new_b_address = client.create_address(b_account_id)['address']
# Point the address at the user
request.user.user_address.b_address = new_b_address
# Save address to current user
request.user.user_address.save()
# Pass the address to the template
context = {'address': new_b_address}
return HttpResponse(json.dumps(context))
in your js
$.ajax({
type: 'POST',
url:'/manage/buy/',
data:{
currency:$('selected').val(),
amount:$('#amount').val(),
csrfmiddlewaretoken:$('input[name=csrfmiddlewaretoken]').val()
},
success: function (response){
console.log(response);
//access the value and print it in console//
var obj=JSON.parse(response)["address"];
alert(obj);
}
})
});
I am using Django to create a form where users will have to enter their email to get notified in the future. After successfully submitting the form I get a post call that refreshes the page that the form is in:
[07/Dec/2017 01:22:41] "POST /notify-email/add/ HTTP/1.1" 200 21
[07/Dec/2017 01:22:42] "POST / HTTP/1.1" 200 23030
Below is the relevant code:
views.py
def add_notify_email(request):
if request.method == "POST":
form = NotifyEmailForm(request.POST)
if form.is_valid():
form.save(commit=True)
return JsonResponse({"status": "success"})
else:
return JsonResponse({"status": "error"})
else:
form = NotifyEmailForm()
return render(request, "landing/home.html", {"form": form})
html
<form class="form" onsubmit="addNotifyEmailInHero()" method="POST" id="notify_email_form_in_hero">
{% csrf_token %}
<div class="form-group row">
<div class="col-sm-10 input-group" id="email_input_in_hero">
<div class="input-group-addon" id="coming_soon_email_icon_in_hero"><i class="fa fa-envelope fa fa-2x" aria-hidden="true" id="email_icon_in_hero"></i></div>
<input class="form-control" type="email" name="email" onkeypress="if(event.keyCode === 13){addNotifyEmailInHero()}" placeholder="If you want to get notified when we go live, please enter your email...." maxlength="255" required id="id_email"/>
</div>
<div class="col-sm-2" id="notify_email_button_in_hero">
<button type="submit" class="btn btn-block btn-primary" id="submit_notify_email_in_hero"><i class="fa fa-bell nav-icon" aria-hidden="true"></i>Notify Me</button>
</div>
</div>
</form>
<script src="coming_soon.js"></script>
coming_soon.js
function addNotifyEmailInHero(e){
var notifyEmailForm = $("#notify_email_form_in_hero");
var thanksModal = $("#thanks");
$.ajax({
type: 'POST',
url: '/notify-email/add/',
data: notifyEmailForm.serialize(),
success: function(res){
alert(res.status);
if(res.status === "success") {
thanksModal.modal('show');
}
else {$("#id_email").val("");
$("#id_email").attr("placeholder", "Please Enter A Valid Email Address");
}
}
})}
What I think is happening:
When you click the button or press enter the form gets "submitted", that means the onsubmit function runs. Specifically, in the js script, you get the form and the modal, then there is an ajax post request with the data from the form to /notify-email/add/.
Based on the urls.py, /notify-email/add/ is connected to the add_notify_email functional view in views.py. This function checks if it is a post request and if the form is valid it saves the data to the database and returns a json response, if the form is not valid it returns also a json response.
If the request is not a post request it renders the specified template.
If add_notify_email returns a json response that gets handled by the js function in coming_soon.js.
That's my understanding of what really is going on.
Because you have an onsubmit handler that ultimately makes an AJAX call to /notify-email/add/, and then the form is submitted to its own handler, which is / because you did not specify a handler.
This is a HTML template that displays all of the proposals in a database (passed through views.py as a list in the dictionary parameter). I then use a jinja for-loop to go through all the proposals in the database and display their attributes.
How can I Post-request the {{ proposal.id }} back to my python code when the "Learn more" button is clicked? I need this to allow me to display the corresponding values in my other html template.
Sorry if this is a basic question, i'm a high school student and extremely new to django! Thanks alot in advance!
{% block body %}
{% for proposal in proposals %}
<div class="jumbotron">
<h2> Proposal : {{ proposal.title }} </h2>
<h4> Status : {{ proposal.status }} </h4>
<h4> Out of --- Votes: </h4>
<div class="progress">
<div class="progress-bar progress-bar-success" style="width: {{ proposal.votes_for }}%">
<span class="sr-only">35% Complete (success)</span>
{{ proposal.votes_for }}% For
</div>
<div class="progress-bar progress-bar-danger" style="width: {{ proposal.votes_against }}%">
<span class="sr-only">10% Complete (danger)</span>
{{ proposal.votes_against }}% Against
</div>
</div>
<p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a></p>
</div>
If you just want to go to the Proposal details you should definitely look to a class-based DetailView.
You can make it with AJAX request or you can make it with form. For the both of types you should have a View to catch it.
HTML Form:
In your template you should have:
<form id="formId" method="post" action="{% url 'catch-proposal' %}">
{% csrf_token %}
<input type="hidden" name="proposal_id" value="{{ proposal.id }}"/>
<p><button type="submit" class="btn btn-primary btn-lg">Learn more</a></p>
<!-- <input type="submit" class="btn btn-primary btn-lg" value="Learn more"/> -->
</form>
It will go to your View from urls.py:
url(r'^post/for/proposal/$', catch_proposal, name='catch-proposal'),
# if your view class-based
# url(r'^post/for/proposal/$', CatchProposal.as_view(), name='catch-proposal')
Then in your view you will catch POST data:
def catch_proposal(request):
if request.method == "POST":
print(request.POST) # have a look for your post params
return reverse_lazy('index') # your response, you can make it on your own
AJAX:
Check it! AJAX and Django
Page uses AJAX without any HTML form
A page makes a POST request via AJAX, and the page does not have an HTML form with a csrf_token that would cause the required CSRF cookie to be sent.
Solution: use ensure_csrf_cookie() on the view that sends the page.
In your scripts define:
function sendPost(proposalId) {
$.ajax({
url: '{% url 'catch-proposal' %}', // or just /ajax/catch/proposal/
method : "POST",
data: {
// your data to send key => val
'id': proposalId
},
dataType: 'json', // it can be xml, json, script, html
success: function (result) {
// Do something if your request was successful (code=200)
// All response data stored in result
console.log(result)
},
error : function(xhr,errmsg,err) {
// Error case
console.log(xhr.status + ": " + xhr.responseText);
}
});
}
For your Learn More button:
<p><button class="btn btn-primary btn-lg" role="button" onclick="sendPost({{ proposal.id }})">Learn more</button></p>
And you will catch it in your View:
#ensure_csrf_cookie # Since you sending POST request without form tag
def catch_proposal(request):
response_data = {} # your response
if request.method == 'POST':
# your post request
if 'id' not in request.POST: # check the param from POST
# Provide error message
response_data['error_message'] = "Can't find ID in POST params..."
else:
# Do whatever
proposal_id = int(request.POST.get('id'))
try:
proposal = Proposal.objects.get(id=transport_id)
response_data['success'] = True
except Proposal.DoesNotExist:
response_data['success'] = False
return JsonResponse(response_data)
else:
response_data = {
'error_message': 'Something is going very strange and wrong...'
}
return JsonResponse(response_data)
Adding created View to urls.py:
from .views import catch_proposal # or yourapp.views
....
url(r'^ajax/catch/proposal/$', catch_proposal, name='catch_proposal'),
....