How to show wtforms validation - python

So after submitting a form, the action is to run a function to validate the input using wtforms, however the error message that i've set did not show up and the whole page went to a TypeError where there is not a return statement which I understand why, but I want to show the error message
So this is the Flask function:
#app.route('/createTodo', methods=['POST', 'GET'])
def a_create_todo():
create_todo_form = CreateTodo(request.form)
if request.method == 'POST' and create_todo_form.validate():
todo_dict = {}
db = shelve.open('todo.db', 'c')
try:
todo_dict = db['Todo']
Todo.count_id = db['Todo_id']
except:
print("Error in retrieving To Do list from Database")
date = datetime.now()
date = date.strftime("%A, %d %B %Y %I:%M%p")
todo = Todo(create_todo_form.text.data, date)
todo_dict[todo.get_todo_id()] = todo
db['Todo'] = todo_dict
db['Todo_id'] = Todo.count_id
db.close()
return redirect(url_for('admin_main'))
HTML:
<form action="{{url_for('a_create_todo')}}" method="POST">
<div class="d-flex">
<input
class="form-control"
id="text"
name="text"
type="text"
value=""
pattern="^(?!\s*$).+"
/>
<button type="submit" class="btn add-button">
<i class="bi bi-plus-circle-fill add-button ps-2"></i>
</button>
</div>
</form>
WTForms python file:
class CreateTodo(Form):
text = StringField('', validators=[
Regexp(regex="^(?!\s*$).+", message="This is a required field")
])
I am expecting an error message but the page went to a TypeError

if you just want to display the validation error in the backend you can just print form.errors like the docs say http://wtforms.simplecodes.com/docs/0.6.2/forms.html#wtforms.form.Form.errors
Also if you want to show the validation error on the html page i think you need to call the form on the template like {{ form.text.errors }}
Note: This other stackoverflow question can help you how to view WTForms validation errors?

Related

Flask: 404 not found, routes to /register.html instead of just /register, can't find the issue

Beginner-ish flask user here, and i can't find where i went wrong.
i am routed to (/register.html) instead of (/register) so i know it's a blunder i made somewhere but everything looks alright to me, a pair of fresh eyes help me out.
Adding this because stackoverflow thinks my question is mostly code and wants me to add more details but i think my question is clear enough so please ignore this part.
#app.route("/register", methods=["POST", "GET"])
def register():
# User reached route via POST (as by submitting a form via POST)
if request.method == "POST":
# Get username and password from form
username_submitted = request.form.get("username")
password_submitted = request.form.get("pasword")
# Query database for user
user = users.query.filter_by(username=username_submitted).first()
# Ensure a username was submitted
if not username_submitted or password_submitted:
flash("must provide username and password", category="error")
return redirect("/register")
# Ensure the username doesn't exist
elif len(user) != 0:
flash("username alredy exists", category="error")
return redirect("/register")
else:
# Hash password
hash = generate_password_hash(password_submitted)
# Insert new user into database
new_user = users(username=username_submitted, password=hash)
try:
db.session.add(new_user)
db.session.commit()
# Redirect user to homepage
return redirect("/")
except:
return "error signing you up"
# User reached route via GET (as by clicking a link or redirect)
else:
return render_template("register.html")
Here's my register form:
{% extends 'layout.html' %}
{% block title %}
Sign up
{% endblock %}
{% block main %}
<form action="/register" method="post">
<div class="form-container">
<div class="form">
<div class="username">
<input type="text" name="username" placeholder="Username" id="username" autofocus/>
</div>
<div class="password">
<input type="password" name="password" placeholder="Password" id="password"/>
</div>
</div>
<input type="submit" name="register" id="register" class="btn" value="Sign up"/>
</div>
</form>
{% endblock %}
I was able to solve this problem a few days ago
the issue was that the href on my anchor tags that link to the login and signup page looked like this:
<a href='login.html'>Login</a>
instead of:
<a href='login'>Login</a>

URL parameter in flask app is showing up 1 form submission late

I have a flask app that I am trying to append a url parameter to after a form submission. For example, I have a date input as a form, and if I selected 2022-02-17 and pressed the button to submit, I would want the url to then append ?date=2022-02-17. My problem is the its always 1 behind. So on first submit, it is only ?date=, and then if I chose another date that isn't 2022-02-17, the url would then update to ?date=2022-02-17. I have a print to make sure the date is being correctly passed into the handler function for that page as well.
Here is a jinja snippet of the form:
<div class="container">
<p>{{ search_date }}</p>
<form action={{ url_for('past_releases', date=search_date) }} method="POST">
<div class="input-group justify-content-center">
<div class="form-outline">
<input class="form-control" type="date" id="searchdate" name="searchdate">
</div>
<button type="submit" class="btn btn-primary">
<i class="fa fa-search"></i>
</button>
</div>
</form>
</div>
and here is the python function for handling that page:
#app.route("/past-releases", methods=["GET", "POST"])
def past_releases():
"""
Search for past releases' info, based on date.
"""
search_date = None
if request.method == "GET" and request.args.get("date"):
search_date = request.args.get("date")
elif request.method == "POST":
search_date = request.form["searchdate"]
# # we use a try here for when someone might click search on the placeholder
# # date of "mm/dd/yyyy"
print(search_date)
if search_date:
try:
date_selcted = dateutil.parser.parse(search_date).strftime("%B %d, %Y")
except ParserError:
return render_template("past_releases.jinja")
pipelines_and_tasks = get_pipeline_runs_by_date(search_date)
return render_template(
"past_releases.jinja",
date_selected=date_selcted,
pipelines_and_tasks=pipelines_and_tasks,
search_date=search_date,
)
return render_template("past_releases.jinja")
I think you should replace the "method" parameter of the form with the GET method. The parameter in url_for is then no longer required. The selected date is automatically added as a URL parameter when the form is submitted.
In the query using request.args, it is also possible to add a standard parameter as the second argument and, specifying the parameter name type, a function to convert the parameter to a date.
The example below illustrates the described possibility.
Flask (app.py)
from datetime import date
from flask import Flask, render_template, request
app = Flask(__name__)
#app.route('/search')
def search():
search_date = request.args.get(
'search-date',
date.today(),
type=date.fromisoformat
)
# Your filter function here.
return render_template('search.html', **locals())
HTML (templates/search.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Search</title>
</head>
<body>
<form method="get">
<input type="date" name="search-date" value="{{search_date.isoformat()}}" />
<input type="submit" />
</form>
</body>
</html>

Flask: redirect to same page after form submission

I have two forms on in my template: one, to post something and the second, to activate file deletion on the server:
<div style="margin-bottom:150px;">
<h4>Delete</h4>
<form method="post" action="/delete">
<div class="form-group">
<input type="hidden" name="delete_input"></input>
</div>
<button type="submit" class="btn btn-danger" id="btnSignUp">Delete</button>
</form>
</div>
<div style="margin-bottom:150px;">
<h4>URLs</h4>
<form method="post" action="/">
<div class="form-group">
<textarea class="form-control" rows="5" id="urls" name="url_area"></textarea>
</div>
<button type="submit" class="btn btn-primary" id="btnSignUp">Urls</button>
</form>
</div>
My app.py looks like this:
#app.route("/")
def main():
return render_template('index.html')
#app.route('/', methods=['POST'])
def parse_urls():
_urls = request.form['url_area'].split("\n")
image_list = get_images(_urls)
return render_template('index.html', images=image_list)
#app.route('/delete', methods=['POST'])
def delete_images():
file_list = [f for f in os.listdir("./static") if f.endswith(".png")]
for f in file_list:
os.remove("./static/" + f)
image_list = []
conn = sqlite3.connect('_db/database.db')
curs = conn.cursor()
sql = "DROP TABLE IF EXISTS images"
curs.execute(sql)
conn.commit()
conn.close()
return render_template('index.html', images=image_list)
Two issues:
I get the form resubmission message when I reload the page after submitting the form
I would like to have one url for everything
The way I see it, I need so use redirects to avoid the duplicate submission and after calling delete, I need to redirect to index.
How can I do this correctly?
I know about redirect and url_for, but how do I redirect to the same page?
You can get the currently requested URL by request.url:
So, to redirect to the same page use:
redirect(request.url)
This worked perfectly for me, in last line:
return redirect(request.referrer)
Change form action to action="{{url_for('delete_images')}}". And for redirection you can use code below:
#app.route('/delete', methods=['POST'])
def delete_images():
return redirect(url_for('delete_images'))
As archer said below:
return redirect(request.referrer)
This is useful when you have a button that uses a route to perform a given function when it is clicked - you don't want to return the user to the URL for that button - you want to return the user to the URL that the button route was referred by, i.e. the page the user was on when they clicked the button.
However, as Mahmoud said:
redirect(request.url)
This is perfect if you perform a function on a page that doesn't use routes or special URLs or anything like that. It essentially just refreshes the page.
One way is set the current url by Javascript in your form. like:
<form method="post" action="/delete">
<div class="form-group">
<input type="hidden" name="delete_input"></input>
</div>
<input type=hidden class=current_url value="" name=current_url>
<button type="submit" class="btn btn-danger" id="btnSignUp">Delete</button>
</form>
And set the hidden input value by JS, like,
function get_current_url() {
var url=window.location.href;
return url
}
$(window).on('load',function(){
var current_url=document.getElementsByClassName('current_url')[0];
current_url.value=get_current_url();
})
At server, redirect to the url that post data

You don't have the permission to access the requested resource error is geven when a post request is sent

I am tring to send a 'POST' request and catch it from python backend. I use flask python framework. Actually, I am doing changes to already developed application.
in the templates I found the code that generate the relavent html.
<div class="body">
<p>
Upload files for the customer {{customer.CustomerName}}.
</p>
<p>
</p>
<form method="POST" action="/admin/customers/{{ customer.ID }}/file_uploading/">
<!-- <input type="file" name="pdfFiles"> -->
<br><br>
<!-- <input type="submit" value="Upload" name="submit"> -->
<button type="submit" class="btn btn-transparent">Upload</button>
</form>
</div>
Here, I try to catch the POST request.
#app.route('/admin/customers/<cust_id>/<action>/', methods=[ 'GET', 'POST' ])
#login_required
def main_admin_customers(cust_id=None, action=None, subaction=None):
if cust_id == None:
c = customers.customer_details()
return render_template('admin_customers_list.html',
customers=c.list_customers())
else:
if cust_id.isdigit():
cust_id = int(cust_id)
c = customers.customer_details(customerid=cust_id)
cust_data = c.retrieve_customer()
if cust_data == None:
return error_message(message='No such customer.')
else:
user = request.cookies['username']
if action == None:
s = scheduling.schedule(customer_id=cust_id)
return render_template('admin_customers_view.html')
# file uploading
# if the action is file_upload and required file is there
# upload it to the file server.
# file url and relevent information should be store in the database
# files will be categorise for each customer from their ID.
elif action == 'file_uploading':
return redirect(url_for('main_admin_customers',
cust_id=cust_id))
# Simple asset creation
elif action == 'create_asset':
pass
However, I get the following error
Forbidden
You don't have the permission to access the requested resource. It is either read-protected or not readable by the server.
However, when I debug the code, as soon as I submit the form, I get the above error message. It didn't even hit the break points at main_admin_customers.
What is the mistake I've done here?
Following page explain the reason. Basically, I need to include csrf token.
https://flask-wtf.readthedocs.org/en/latest/csrf.html
If the template has a form, you don’t need to do any thing. It is the same as before:
<form method="post" action="/">
{{ form.csrf_token }}
</form>
But if the template has no forms, you still need a csrf token:
<form method="post" action="/">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
</form>

Django Form Submission Failing

I'm working on a little app that allows you to save specific location information about places you've been. The issue I'm having is that clicking the submit button on the 'save new location' page doesn't seem to be doing much of anything. It redirects to .../locationlib/savenew/, which is supposed to be the url that saves the form input as a new model object, but both according to debugging print statements and what actually happens, that function is just never called. I've had success with other forms using django but this one seems to be tripping me up. Can someone give me an idea of what's going on here?
views.py
def new(request):
return render(request, 'locationlib/new.html')
def savenew(request):
print 'savenew called'
name = request.POST['name']
latitude = float(request.POST['latitude'])
longitude = float(request.POST['longitude'])
desc = request.POST['description']
user = User.objects.get(username=str(request.POST['user']))
print 'all variables set'
l = Location(
name=name,
longitude=longitude,
latitude=latitude,
custDescription=desc,
user=user,
)
print 'l defined'
l.save()
print 'l saved'
return HttpResponseRedirect(reverse('locationlib:detail', args=[l.id]))
new.html
<div id='new-location-form'>
<form action="{% url 'locationlib:savenew' %}" method="post">
{% csrf_token %}
Name: <input type='text' name='name' value='Place Name' required><br>
User: <input type='text' name='user' value='User' required><br>
Longitude: <input type='text' name='longitude' value="Longitude Coordinate" required><br>
Latitude: <input type='text' name='latitude' value='Latitude Coordinate' required><br>
Description: <textarea name='description'>Description of Place</textarea><br>
<input type="submit" value="Save">
</form>
</div>
urls.py
urlpatterns = patterns( '',
...
url(r'new/', views.new, name='new'),
url(r'^savenew/', views.savenew, name='savenew'),
)
Your first URL pattern, new, is not anchored to the start of the string. That means that it matches anything that ends with "new" - and that includes "savenew". So your request for "savenew" is being caught by that pattern, and being sent to the new view.
Just put a ^ character at the front, as you have done with the other pattern.
try to use Modelforms
forms.py:
from django.forms import ModelForm
from myapp.models import Location
# Create the form class.
class LocationForm(ModelForm):
class Meta:
model = Location
view.py
def savenew(request):
if request.method == 'POST':
form = LocationForm(request.POST)
if form.is_valid():
new=form.save()
return HttpResponseRedirect(reverse(reverse('locationlib:detail', args=[new.id])))
return render(request,'reservetion/sign.html',{'form': form})
else:
form = SignForm()
return render(request, 'reservetion/sign.html',{'form': form})
new.html
<form action="{% url 'locationlib:savenew' %}" method="post">
{% csrf_token %}
{{ form}}
</form>

Categories