I am learning flask and made a small application. Now I am trying to learn form. I used a simple code to validate a name request and it should give error when the field remains empty. But it isn't giving one.
Main file :
from flask import Flask, render_template
from flask.ext.moment import Moment
from flask.ext.wtf import Form
from wtforms import StringField, SubmitField, validators
import requests
import json
from datetime import datetime
app = Flask(__name__)
app.config['SECRET_KEY'] = 'abcd'
moment = Moment(app)
class Nameform(Form):
name = StringField("whats your name?", [validators.Required()])
submit = SubmitField('submit')
#app.route('/')
#app.route('/index')
def index():
api_call = requests.get('https://api.stackexchange.com/2.2/users/moderators?order=desc&sort=reputation&site=stackoverflow') # api call to stack for user with highest scores in des order
var_1 = json.loads(api_call.text)
var_2 = [{'link': value['link'], 'name': value['display_name'], 'user_id': value['user_id']} for value in var_1['items']]
return render_template('index.html', posts=var_2, current_time=datetime.utcnow())
#app.route('/user/<id>/<user_name>')
def user(id, user_name):
print id
api_call = requests.get('https://api.stackexchange.com//2.2/users/'+id+'/reputation?site=stackoverflow') # api call for reputation of click user
var_1 = json.loads(api_call.text)
return render_template('reputation.html', result=var_1, user_name=user_name)
#app.route('/test', methods=['GET', 'POST'])
def user_form():
name = None
form = Nameform()
if form.validate_on_submit():
name = form.name.data
form.name.data = ''
return render_template('test_form.html', form=form, name=name)
if __name__ == '__main__':
app.run(debug=True)
Template for rendering:
<div class="page-header">
<h1>Hello, {% if name!= None %}{{ name }}{% else %}Stranger{% endif %}!</h1>
</div>
<form method=post>
{{ form.name.label }} {{ form.name() }}
{{ form.submit() }}
</form>
Why it is not throwing any error? when the field remains empty
You can render the error messages using form.errors. Note that you're also missing your CSRF token, which is required for validation since you didn't disable WTF_CSRF_ENABLED, so I've added {{ form.csrf_token }}. See CSRF Protection.
<div class="page-header">
<h1>Hello, {% if name!= None %}{{ name }}{% else %}Stranger{% endif %}!</h1>
</div>
{% for field in form.errors %}
{% for error in form.errors[field] %}
<div class="error">{{ error }}</div>
{% endfor %}
{% endfor %}
<form method=post>
{{ form.csrf_token }}
{{ form.name.label }} {{ form.name() }}
{{ form.submit() }}
</form>
I think you have not included the novalidate attribute with form.
The novalidate attribute is used to tell the web browser to not apply validation to the fields in this form, which effectively leaves this task to the Flask application running in the server.
For Sample code
Related
I have a form to kick off generation of a few different types of files, and each type has different input parameters. I'd like to use a generic form, and based on the type selected, show only the relevant parameters.
The python code is
from flask_wtf import FlaskForm
from wtforms import SelectField, DecimalField, IntegerField
from wtforms.validators import Optional, InputRequired
from flask import Blueprint, redirect, render_template, url_for
bp = Blueprint("routes", __name__)
class genericForm(FlaskForm):
# populate the file types, with default option empty
fileType = SelectField("File type",
choices=[(None, ""),
("file1", "file1"),
("file2", "file2"),
("file3", "file3")],
validators=[DataRequired()])
required1 = IntegerField("Required 1", validators=[InputRequired()])
required2 = IntegerField("Required 2", validators=[InputRequired()])
optional1 = DecimalField("Optional 1", validators=[Optional()])
optional2 = DecimalField("Optional 2", validators=[Optional()])
optional3 = DecimalField("Optional 3", validators=[Optional()])
optional4 = DecimalField("Optional 4", validators=[Optional()])
#bp.route("/index", methods=["GET", "POST"])
def index():
"""The index"""
form = genericForm()
if form.validate_on_submit():
identifier = createFile(form)
return redirect(url_for("routes.files", info=identifier))
# which file types require which form fields
fileFields = {"file1": "optional1", "optional3",
"file2": "optional2", "optional3",
"file4": "optional1", "optional4"}
return render_template("index.html", form=form, fileFields=fileFields)
and the jinja template I currently have is
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}File Creation{% endblock %}</h1>
{% endblock %}
{% block content %}
<form action="" method="post" novalidate>
{{ form.hidden_tag() }}
<p>
{% for field in form %}
{% if field.flags.required %}
{{ field.label }} {{ field }}<br />
{% endif %}
{% endfor %}
</p>
<p>{{ form.submit() }}</p>
</form>
{% endblock %}
But what I'm trying to do is determine whether to hide a field based on whether it's required or if it's in the list for the selected fileType, and update that when the selected fileType changes
This question already has an answer here:
Form is never valid with WTForms
(1 answer)
Closed 3 years ago.
I have like a search bar built with wtforms, but I don't know for some reason I am unable to fetch data or value entered in the search bar. I tried almost everything from online tutorials but just couldn't find the solution. Below is my code examples.
For my search bar, I have tried form.search.data and request.form['search'] too but it's not working.
#flask_app.py
from flask import Flask, render_template, url_for, flash, redirect, request
from forms import SearchForm
app = Flask(__name__)
app.config['SECRET_KEY'] = '791628bb0b13ce0c676dfde'
#app.route('/')
#app.route('/home')
def home():
return render_template('home.html')
#app.route('/search', methods= ['GET', 'POST'])
def search():
form = SearchForm()
if form.validate_on_submit():
if form.search.data == 'admin':
return redirect(url_for('home'))
return render_template('search.html', form= form)
if __name__ == "__main__":
app.run(debug=True)
#forms.py
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
class SearchForm(FlaskForm):
search = StringField('search', validators=[DataRequired()])
submit = SubmitField('Search')
#search.html
{% extends 'layout.html' %}
{% block content %}
<form action="" class="form-inline mt-4" method="POST">
<div class="form-group">
{{form.search(class= 'form-control', placeholder= 'Enter term to search')}}
{{form.submit(class= 'btn btn-outline-primary')}}
</div>
</form>
{%endblock%}
#home.html
{% extends 'layout.html' %}
{% block content %}
<h1>Welcome to homepage!</h1>
{%endblock%}
Is there any bug? I expect form.search.data should be able to fetch data from search bar.
You're not inserting the CSRF field in the HTML form which can be done via form.hidden_tag()
#search.html
{% extends 'layout.html' %}
{% block content %}
<form action="" class="form-inline mt-4" method="POST">
{{ form.hidden_tag() }}
<div class="form-group">
{{form.search(class='form-control', placeholder= 'Enter term to search')}}
{{form.submit(class='btn btn-outline-primary')}}
</div>
</form>
{% endblock %}
See this
I'm trying to raise an error in Jinja2, in a WTForm, the error should be raised if url input is not validated, but when i submit an invalide url, i get a popup saying "Please enter a url".
how do i pass the default popup and add a custom error message ?
here is the main py:
from datetime import datetime
from flask import Flask, render_template, url_for, request, redirect,flash
from logging import DEBUG
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField
from flask.ext.wtf.html5 import URLField
from wtforms.validators import DataRequired , url
app = Flask(__name__)
app.logger.setLevel(DEBUG)
app.config['SECRET_KEY']='{#\x8d\x90\xbf\x89n\x06%`I\xfa(d\xc2\x0e\xfa\xb7>\x81?\x86\x7f\x1e'
#app.route('/')
#app.route('/index')
def index():
return render_template('base.html')
#app.route('/add', methods=['GET','POST'])
def add():
return render_template('add.html')
# HERE IS THE LOGIN FORM
class Login(FlaskForm):
username = StringField('username')
password = PasswordField('password')
url = URLField('url', validators=[DataRequired(),url()])
#app.route('/form', methods=['GET','POST'])
def form():
form = Login()
if form.validate_on_submit():
url = form.url.data
return redirect(url_for('index'))
return render_template('form.html',form = form )
if __name__ =='__main__':
app.run(debug=True)
and here is the template:
<!DOCTYPE html>
<html>
<head>
<title>form</title>
</head>
<body>
<h1>Hello !</h1>
<form method="POST" action="{{url_for('form')}}">
{{ form.hidden_tag() }}
{{ form.csrf_token }}
{{ form.username.label }}
{{ form.username }}
{{ form.password.label }}
{{ form.password }}
{{ form.url.label }}
{{ form.url }}
{% if form.url.errors %} <p> {{error}}</p> {% endif %}
<button type="submit">Submit</button>
</form>
</body>
</html>
Because you're using the data type URLField, this is rendered as a HTML5 "url" form field type.
Your browser recognises this and performs its own validation on the data submitted:
There is no way for you to override this - it's built in to the browser.
If you need to show a custom error message, you might be able to use a TextField instead, and provide your own URL validation logic.
Add your own message instead of default message in your form defination.
url = URLField('url', validators=[DataRequired(),url(message="Please enter a valid url (e.g.-http://example.com/)")])
As Matt Healy before mentiones, it is the browser that validates URLField.
So if you want a custom error message use StringField (TextField is outdated). If required, a custom message can be used as shown below message='text to display'.
Example:
class XYZForm(FlaskForm):
url = StringField('url', validators=[DataRequired(),url(message='Please enter valid URL')])
description = StringField('description')
Of course the *.html should include code to output an error to the page:
<ul>
{% for error in form.url.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
It seems like novalidate attribute works for your case.
I have been struggling around the WTF forms for quite a while now. But this error, never seems to go away. I When ever I try to run this code the form never validates
Views :
#bundle.route('/content/add/', methods=['GET', 'POST'])
#bundle.route('/content/add', methods=['GET', 'POST'])
#bundle.route('/content/edit/<posturl>/', methods=['GET', 'POST'])
#bundle.route('/content/edit/<posturl>', methods=['GET', 'POST'])
#fas_login_required
def addcontent(posturl=None):
form = CreateContent()
form_action = url_for('content.addcontent')
if posturl is not None:
content = Content.query.filter_by(slug=posturl).first_or_404()
form = CreateContent(obj=content)
if form.slug.data == posturl and request.method == 'POST' and form.validate():
form.populate_obj(content)
db.session.commit()
return redirect(url_for('content.addcontent',
posturl=posturl, updated="True"))
else:
if request.method == 'POST' and form.validate():
query = Content(form.title.data,
form.slug.data,
form.description.data,
form.media_added_ids.data,
form.active.data,
form.tags.data,
g.fas_user['username'],
form.type_content.data
)
try:
db.session.add(query)
db.session.commit()
# Duplicate entry
except Exception as e:
return str(e)
return redirect(url_for('content.addcontent',
posturl=form.slug.data, updated="True"))
else:
print "Please validate form"
return render_template('content/edit_content.html', form=form,
form_action=form_action, title="Create Content")
Form Class :
# -*- coding: utf-8 -*-
from flask.ext.wtf import Form
from wtforms import TextField, TextAreaField
from wtforms import BooleanField, SelectField, validators
from wtforms.validators import Required
__all__ = ['CreateContent']
class CreateContent(Form):
title = TextField(
'Title', [validators.Length(min=4, max=255)])
slug = TextField(
'Url-Slug', [validators.Length(min=4, max=255)])
description = TextAreaField('Content', [validators.Length(min=4)])
media_added_ids = TextField('media')
type_content = SelectField(u'Content Type',
[Required()],
choices=[('blog', 'Blog Post'),
('media', 'Lecture'),
('doc', 'Documentation')]
)
# Comma seprated media id's
active = BooleanField('Published')
tags = TextField('Tags', [Required()])
# Comma seprated tag id's
And my Template :
{% extends "base.html" %}
{% block title %}
{{ title }}
{% endblock %}
{% block content %}
{% from "_formhelpers.html" import render_field %}
<div id="Create Content">
<center><h3> {{ updated }} </h3></center>
<h2>{{ title }}</h2>
<form method="post" action="">
<fieldset>
<legend></legend>
{{ render_field(form.title) }}
{{ render_field(form.slug ) }}
{{ render_field(form.description ) }}
{{ render_field(form.media_added_ids)}}
{{ render_field(form.type_content) }}
{{ render_field(form.active) }}
{{ render_field(form.tags )}}
</fieldset>
<input type="submit" class="button" value="Save"/>
</form>
</div>
{% endblock %}
Any help will be highly apprieciated
If the CSFR tokens are activated in a flask application setting, a CSFR token is included in each form. If developer has activated the setting and not included it in the form template the flask WTF would automatically reject the request.
The solution to this problem was to the following tag :
{{form.hidden_tag()}}
Once added, a CSFR id is included in the request and sent to the views for validation by the WTForms.
If you haven't included this token no errors will appear in the form.errors dictionary. If you iterate over this dictonary no errors will be show, but the form.validate method will return false.
Flask-WTF adds the CSRF token automatically if it is activated in your Flask settings. If this setting is active and it isn't included in the form submission the submission will be rejected. The solution in this case is to add the hidden_tag field in the template so that it is included in the form submission.
{{form.hidden_tag()}}
Flask-WTF's documentation states that:
Flask-WTF provides you a FileField to handle file uploading, it will automatically draw data from flask.request.files if the form is posted. The data attribute of FileField will be an instance of Werkzeug FileStorage.
However, when I use FileField, the data attribute after posting is not a FileStorage object. Instead, it is either None (if I define my form with enctype="multipart/form-data" as suggested in the documentation) or the filename as a string (if I don't define an enctype).
Here is the relevant Jinja2 template:
{% from "_form.html" import render_field %}
{% block body %}
<section class="page-width-container" id="offset-content">
<div id="utility-box">
<h1 class="utility-header">Settings</h1>
{{ message }}
<form action="/settings" method="post" enctype="multipart/form-data">
{{ render_field(form.photo) }}
<input type="submit" class="form-action" value="Save" />
</form>
</div>
</section>
{% endblock %}
The _form.html macro:
{% macro render_field(field) %}
<div class="field">
{{ field.label }}
<span class="form-input">{{ field()|safe }}</span>
<div class="clear"></div>
{% if field.errors %}
<ul class="errors">
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
{% endmacro %}
And here is the Python:
from flask import Blueprint, render_template, request
from flask.ext.wtf import Form
from flask.ext.wtf.file import FileField, FileRequired
settings_bp = Blueprint('settings', __name__)
class SettingsForm(Form):
photo = FileField('Photo', validators=[
FileRequired('no file!'),
])
#settings_bp.route('/settings', methods=['GET', 'POST'])
def login():
form = SettingsForm(request.form)
form.validate_on_submit()
print(type(form.photo.data))
return render_template('settings.html', form=form, message=form.photo.data)
With the enctype attribute on the form element, the printed type is NoneType; without the enctype attribute, the printed type is unicode. Neither is the FileStorage object advertised by the documentation.
I looked into the source code (form.py) and figured out the issue: the code to replace the default data attribute with the FileStorage object is not activated if the form is initialized with a specific formdata. The formdata must be left unspecified in order for the file functionality to work. Replacing this line:
form = SettingsForm(request.form)
With this line:
form = SettingsForm()
Resolved the issue.
An alternative to clearing the passed form data
from form = SettingsForm(request.form) to form = SettingsForm()
is to pass both the request.form and the request.files as suggested by the flask-wtf documentation .
from flask import request
from werkzeug.datastructures import CombinedMultiDict
form = SettingsForm(CombinedMultiDict((request.files, request.form)))