AJAX POST Flask WTForm without refreshing the page - python

Good day, I have a simple web page with an email form in it. I'm trying to collect the data from it and populate a database without refreshing the template. Here is my code so far:
Form:
from flask_wtf import Form
class EmailForm(Form):
email = StringField('Email Address', [
DataRequired(message='Required field'),
Email(message='Please provide a valid email address')
])
submit = SubmitField('send')
Route:
#app.route('/', methods=('GET', 'POST'))
def index():
form = EmailForm(request.form)
if request.method == 'POST' and form.validate_on_submit():
try:
email = Email(form.data['email'])
db.session.add(email)
db.session.commit()
except IntegrityError as e:
app.logger.info(e)
return redirect(url_for('index'))
return render_template('index.html', form=form)
Ajax:
$(function() {
$('#email_submit').bind('click', function() {
$.getJSON('/', {
email: $('input[name="email"]').val()
});
return false;
});
});
Template:
<form name="collectEmail" id="collectForm" method="post" action="{{ url_for('index') }}">
{{ form.hidden_tag() }}
{{ form.csrf_token }}
{% if form.csrf_token.errors %}
<div class="warning">You have submitted an invalid CSRF token</div>
{% endif %}
<div class="input-group">
{{ form.email(class='form-control', placeholder='Your Email *', type='email')}}
<p class="help-block text-danger"></p>
<span class="input-group-btn">
{{ form.submit(class='btn btn-primary', id='email_submit', type='submit') }}
</span>
</div>
</form>
The database successfully populates; but, I would like to avoid refreshing the page after submitting the form.

You are not sending the request with AJAX, #email_submit is an input of type submit, not a button, so if you don't use preventDefault() you end up executing the default behaviour of that input.
You have 2 options there, one is using preventDefault() and the other is to switch that input to a button, so it won't submit the form before the javascript code runs.

Related

How do I submit a form without page reload using Ajax

I'm learning Ajax on how I can submit a comment form without page reload. I'm using Django, I have list of posts in homepage each with comment form. When I submit a comment it is not saving in database and also it doesn't display in browser. When I check on Chrome console, I got an error 2 elements with non-unique id.
from django.http import JsonResponse
from django.template.loader import render_to_string
def home(request):
all_images = Post.objects.filter(poster_profile=request.user)
if request.method == 'POST':
post_id = request.POST.get("post_comment")
post_obj = Post.objects.get(pk=post_id)
form = CommentForm(request.POST)
if form.is_valid():
comment=form.save(commit=False)
comment.user=request.user
comment.commented_image=post_obj
comment.save()
else:
form=CommentForm()
context = {'all_images':all_images, 'form':form}
if request.is_ajax():
html=render_to_string('ajax_feeds_comment.html', context, request=request)
return render(request, 'home.html', context)
#home.html
{% for post in all_images %}
<img src="{{ post.image.url }}">
{% for comment in post.comments.all %}
<p>{{ comment.comment_post }}</p>
{% endfor %}
<div class="reload-form">
{% include "ajax_feeds_comments.html" %}
</div>
{% endfor %}
#ajax_feeds_comments.html
<form method="POST" class="feeds-comment" action=".">
{% csrf_token %}
<input type="hidden" value="{{post.id}}" name="post_comment">
<textarea name="comment_post" class="form-control" id="id_comment_post{{post.id}}"></textarea>
<button type="submit" class="btn btn-primary">submit</button>
</form>
<script>
$(document).on('submit', '.feeds-comment', function(event){
event.preventDefault();
console.log($(this).serialize());
$.ajax({
type: 'POST',
url: $(this).attr('action'),
data: $(this).serialize(),
dataType: 'json',
success: function(response){
$('.reload-form').html(response['form']);
},
error: function(rs, e) {
console.log(rs,responseText);
},
});
});
</script>
There are multiple things not correct here:
In your "urls.py" you should not send every request to your views. This is where you get the "favico.ico" error 500 get from. Having no favico is ok, but getting a Error 500 is not.
Check the html code for duplicate id's! These have to be unique.
If you use django variables for html, do it like this: Instead of "{{post.id}}" use: "{{ post.id }}" with spaces around the var.
In the
document.on("submit", "feeds-comment", function(){...})
you're not using the id of that element but it's class name.
Check where the submit is going to. Check Django if the request is being handled. (You see that in the console where Django is running). Also maybe Post a screenshot here!

Failure in multiple times Parameters Passing in Django (Python)

I am new to django. My current plan is displaying user name on different html pages, once user have successfully logged in. At the moment, the page after login page can successfully display the user name with the django tag in html which is {{ username }}. But once it has successfully passed to my second page, my second page CAN NOT pass it to my third page. The attached images are my html codes for second.html and third.html. Thanks for your help.
Second.html
<form action="/SecondPageSub/" method="POST">
{% csrf_token %}<br>
<b>NTID:</b><br>
<label name="usrnm">{{username}}</label>
<button type="submit" name="SecondPageSub">
SUBMIT
</button>
</form>
Third.html
<form action="/ThirdPageSub/" method="POST">
{% csrf_token %}<br>
<b>NTID:</b><br>
<label name="usrnm">{{username}}</label>
<button type="submit" name="ThirdPageSub">
SUBMIT
</button>
</form>
Python codes in view.py
def ActionGet(request):
if request.method == "POST":
if 'login' in request.POST:
usrname = request.POST.get("usrnm", None)
passwrd = request.POST.get("pwd", None)
dic={}
dic['username']=usrname
dic['password']=passwrd
return render(request, "Second.html",dic)
if 'SecondPageSub' in request.POST:
usrname = request.POST.get("usrnm", None)
dic={}
dic['username']=usrname
return render(request, "Third.html",dic)
if 'ThirdPageSub' in request.POST:
usrname = request.POST.get("usrnm", None)
dic={}
dic['username']=usrname
return render(request, "Forth.html",dic)
by default django gives you {{ request.user.username }} through out your templates. So you can call it on any templates
You aren't passing the usrnm in your post request with SUBMIT on your SecondPageSub

Flask form not displaying errors when I use custom validators

I'm creating custom validators for my Flask form inputs and as an example, I've defined a function below that checks whether you've included "s3://" in the input, and if not, I want it to throw an error.
When I test this out and omit the "s3://" in the input, no error is thrown. What am I doing wrong?
Form class:
from flask_wtf import FlaskForm
from wtforms import StringField, TextField, SubmitField, IntegerField, SelectField, validators
from wtforms.validators import ValidationError
import boto3
# CUSTOM VALIDATOR
def is_s3_uri(form, field):
if "s3://" not in field.data:
raise ValidationError('Bucket name must contain full path. Missing "s3://".')
class InputForm(FlaskForm):
input_uri = StringField('INPUT BUCKET', validators=[validators.required(), is_s3_uri])
output_uri = StringField('OUTPUT BUCKET', validators=[validators.required(), is_s3_uri])
HTML:
<div class="tab">
<h3>File Locations:</h3>
<div class="form-group required">
{{ form.input_uri.label }}: {{ form.input_uri(placeholder="(e.g. s3://pipeline-run/fastqs/)...", oninput=", this.className = ''") }}
</div>
{% if form.input_uri.errors %}
<ul class="errors">{% for error in form.input_uri.errors %}<li>{{ error }}</li>{% endfor %}</ul>
{% endif %}
<div class="form-group required">
{{ form.output_uri.label }}: {{ form.output_uri(placeholder="(e.g. s3://pipeline-run/results/)...", oninput="this.className = ''") }}
</div>
{% if form.input_uri.errors %}
<ul class="errors">{% for error in form.output_uri.errors %}<li>{{ error }}</li>{% endfor %}</ul>
{% endif %}
</div>
UPDATE:
I also tried adding a built-in validator (e.g. with Length(min=6, max=120)), but that doesn't get enforced either.
stack_name = StringField('STACK NAME', validators=[Length(min=4, max=120), DataRequired()])
UPDATE 2:
#app.route('/', methods=['GET', 'POST'])
def pipeline():
INPUT_URI = ''
OUTPUT_URI = ''
form = InputForm(request.form)
if request.method == 'POST':
if form.validate_on_submit():
INPUT_URI = request.form['input_uri']
OUTPUT_URI = request.form['output_uri']
# process info
else:
flash('Invalid params. Please re-try.', 'danger')
return redirect(request.path)
return render_template('pipeline-alt.html',
title='Pipeline Input',
form=form)
UPDATE 3:
Using the Jquery Form Validation Plugin
HTML:
<div class="form-group required">
{{ form.input_uri.label }}: {{ form.input_uri(placeholder="(e.g. s3://pipeline-run/fastqs/)...", oninput=", this.className = ''", name="input_uri") }}
</div>
JQuery:
<script>
$(document).ready(function () {
$('#regForm').validate({ // initialize the plugin
rules: {
input_uri: {
required: true,
minlength: 4
}
},
submitHandler: function (form) { // for demo
alert('valid form submitted'); // for demo
return false; // for demo
}
});
});
</script>
Result: TypeError: html_params() got multiple values for keyword argument 'name'
Are you validating your form on submit? You didn't post enough code but I will try to help.
In the view that renders you should use form.validate_on_submit() in order for you form to get validated. I don't see your entire form code in the HTML you posted and if this is it, then you are missing a submit button that will send the form to the server for validation.
Please post your view (that renders the form) code
Your form should use POST in the method for the example below.
An example of what your view that handles the form should llok like with form.validate_on_submit()
from flask import request
#app.route('/form_route', methods=['GET', 'POST'])
def form_route():
form = YourFormClass()
if request.method == 'POST':
if form.validate_on_submit():
# validation will be triggered from the if statement and if it validates, it will enter this if block
# perform actions if the form is valid
else:
# do somthing if the form is not valid
return render_template(form=form)

Django view is not redirecting properly

I'm facing some difficulties to render pages with Django shortcuts. My workflow is quite simple:
Users go a page where they can view some project data (detail_project)
If they want to update the project data, they should click a button that will send a POST request and loads update_project page. This page is loaded with current project data.
Users update and submit new data. After submission, they are returned to detail_project page.
My view is like this:
def update_project(request):
if request.method == 'POST':
if 'update_project_submit' in request.POST:
# updates project with form data and returns to detail project page
return redirect('detail_project', project_name=project_name)
else:
# loads project original data into the form
return render(request, 'project/update_project.html', context)
def detail_project(request, project_name):
if request.method == 'POST':
if 'update_project' in request.POST:
return update_project(request)
else:
# does another stuff
else:
# shows project details
return render(request, 'project/detail_project.html', context)
urls.py:
url(r'^project/update/$', views.update_project, name='update_project'),
url(r'^project/details/(?P<project_name>[a-zA-Z][a-zA-Z0-9-_]+)/$', views.detail_project, name='detail_project'),
And update_project.html:
<form class="form-horizontal" role="form" action="" method="post">
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<div class="col-sm-3">
<label for="{{ field.id_for_label }}" class="control-label">{{ field.label }}</label>
</div>
<div class="col-sm-9">
{{field}}
</div>
<div class="col-sm-12">
{{ field.help_text }}
</div>
</div>
{{field.non_field_errors }}
{{field.errors}}
{% endfor %}
<button type="submit" name="update_project_submit" class="btn btn-primary">Submit</button>
</form>
[ Update ]
Forms.py
class UpdateProjectForm(forms.Form):
project_name_validator = RegexValidator(r'^[a-zA-Z][a-zA-Z0-9-_]{1,31}$', constants.PROJECT_NAME_INVALID)
project_name = forms.CharField(required=True, widget=forms.TextInput(attrs={'required': 'true'}), validators=[project_name_validator])
project_description = forms.CharField(required=True, widget=forms.Textarea(attrs={'style':'resize:none;', 'required': 'true'}))
project_expiration = forms.DateField(required=False, widget=forms.TextInput(attrs={'class':'datepicker'}))
def __init__(self, *args, **kwargs):
super(UpdateProjectForm, self).__init__(*args, **kwargs)
self.fields['project_name'].label = "Project Name:"
self.fields['project_description'].label = "Project Description:"
self.fields['project_expiration'].label = "Expiration Date:"
The problem is that I cannot update my project. My page loads the form properly (update_project) with the current data (step 2), but when I submit it (click the Submit button, I'm redirected to detail project page without entering the if 'update_project_submit' in request.POST: statement. Maybe my workflow is wrong. I cannot figure it out.
I printed my request, and I've really cofirmed that when I submit the form, I'm receiving a POST request to detail_project.
Is there something I am missing? Or am I trying to do something wrong according to Django's logic?
Use
if request.POST.get('update_project_submit', False):
instead of
if 'update_project_submit' in request.POST:

How to show confirmation modal in Flask app after form submission?

I'm trying to show a confirmation/success message to the user in my Flask app, but I can't figure out how to display it in a modal.
#app.route("/", methods=["POST"]
def sendForm():
form = ContactForm(request.form)
if request.method == 'POST':
if form.validate():
# do stuff with form data
return render_template("contact.html", form=form)
else:
# display error message
else:
return render_template("index.html")
The part where I return the contact.html template is where I need help, I think. Because that page is basically refreshed and shown again after the POST request successfully completes. Need to display a confirm message to the user in a modal instead.
On the front-end, my form is looks like this:
<form method="POST" action="{{ url_for('sendForm') }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
{{ render_field(form.email) }}
{{ render_field(form.name) }}
<input id="submit-form" type="submit" value="Send">
</form>
I would do some form of this...
Pass a boolean in your render_template:
submission_successful = True #or False. you can determine this.
render_template("contact.html", form=form, submission_successful=submission_successful))
Then in your template place an if statement
{% if submission_successful %}
// modal elements here
{% endif %}

Categories