I am using WTForms, and I have a problem with hidden fields not returning values, whereas the docs say they should. Here's a simple example:
forms.py:
from wtforms import (Form, TextField, HiddenField)
class TestForm(Form):
fld1 = HiddenField("Field 1")
fld2 = TextField("Field 2")
experiment.html:
{% from "_formshelper.html" import render_field %}
<html>
<body>
<table>
<form method=post action="/exp">
{% for field in form %}
{{ render_field(field) }}
{% endfor %}
<input type=submit value="Post">
</form>
</table>
</body>
</html>
(render_field just puts the label, field and errors in td tags)
experiment.py:
from flask import Flask, request, render_template
from templates.forms import *
from introspection import *
app = Flask(\__name__)
app.config.from_object(\__name__)
db_session = loadSession()
#app.route('/exp', methods=['POST', 'GET'])
def terms():
mydata = db_session.query(Peter).one()
form = TestForm(request.form, mydata)
if request.method == 'POST' and form.validate():
return str(form.data)
return render_template('experiment.html', form = form)
if __name__ == '__main__':
app.run(debug = True)
mydata returns the only row from a table that has 2 fields, fld1 and fld2. fld1 is an integer autoincrement field. The form is populated with that data, so if I run experiment.py, when I submit the form I get:
{'fld2': u'blah blah blah', 'fld1': u'1'}
But if I change fld1 to HiddenField, when I hit submit, I get:
{'fld2': u'blah blah blah', 'fld1': u''}
What am I doing wrong?
I suspect your hidden field is either (1) not getting a value set, or (2) the render_field macro isn't building it correctly. If I had to bet, I'd say your "mydata" object doesn't have the values you expect.
I stripped your code down to the bare minimum, and this works for me. Note I am explicitly giving a value to both fields:
from flask import Flask, render_template, request
from wtforms import Form, TextField, HiddenField
app = Flask(__name__)
class TestForm(Form):
fld1 = HiddenField("Field 1")
fld2 = TextField("Field 2")
#app.route('/', methods=["POST", "GET"])
def index():
form = TestForm(request.values, fld1="foo", fld2="bar")
if request.method == 'POST' and form.validate():
return str(form.data)
return render_template('experiment.html', form = form)
if __name__ == '__main__':
app.run()
and
<html>
<body>
<table>
<form method=post action="/exp">
{% for field in form %}
{{field}}
{% endfor %}
<input type=submit value="Post">
</form>
</table>
</body>
</html>
This gives me {'fld2': u'bar', 'fld1': u'foo'} as I would expect.
Check that mydata has an attribute "fld1" and it has a value. I might set it explicitly like form = TestForm(request.values, obj=mydata) - it doesn't look like WTForms would care, but I've gotten burned by it being weirdly picky sometimes.
If that doesn't work for you, come back and post your HTML and what values mydata has.
Related
please tell me how to access min_entries correctly, I need it to generate fields for the form.
My codes:
forms.py:
class ToSend(FlaskForm):
send = FieldList(FormField(AddEquipment), min_entries=())
equipment_add.py:
#app.route('/equipment_add', methods=['GET', 'POST'])
def addEquipment():
update = 0
if request.method == "POST":
update = int(request.form['update'])
print(update)
form = ToSend()
form.send.min_entries = update
return render_template('equipment_add.html', form=form, update=update)
return render_template('equipment_add.html', update=update)
And so I turn form.send.min_entries = update and there's a mistake:
while len(self.entries) < self.min_entries:
TypeError: '<' not supported between instances of 'int' and 'tuple'
What am I doing wrong?
The problem arises because you didn't specify an integer as the value for the minimum number when defining the FieldList, but a pair of brackets.
If you don't enter a value here, the default value of 0 will be used automatically.
As I understand your code, you want to dynamically add form fields depending on the number requested by another form.
You can solve your problem in different ways. I'll show you two options, using a GET request to query how many fields the form should contain.
The first option is to add the fields manually using append_entry().
from flask import (
Flask,
render_template,
request
)
from flask_wtf import FlaskForm, Form
from wtforms import FieldList, FormField, StringField, SubmitField
app = Flask(__name__)
app.secret_key = 'your secret here'
class EquipmentForm(Form):
name = StringField('Name')
class AddEquipmentForm(FlaskForm):
items = FieldList(FormField(EquipmentForm))
submit = SubmitField('Add Items')
#app.route('/equipment/add', methods=['GET', 'POST'])
def equipment_add():
num_fields = max(0, request.args.get('cnt', 0, type=int))
form = AddEquipmentForm(request.form)
form.items.min_entries = num_fields
while len(form.items.entries) < form.items.min_entries:
form.items.append_entry()
if form.validate_on_submit():
print(form.items.data)
return render_template('equipment_add.html', **locals())
The second option uses a factory to recreate the form for each request and sets the min_entries attribute of the FieldList.
from flask import (
Flask,
render_template,
request
)
from flask_wtf import FlaskForm, Form
from wtforms import FieldList, FormField, StringField, SubmitField
app = Flask(__name__)
app.secret_key = 'your secret here'
class EquipmentForm(Form):
name = StringField('Name')
class AddEquipmentForm(FlaskForm):
submit = SubmitField('Add Items')
#app.route('/equipment/add', methods=['GET', 'POST'])
def equipment_add():
def _factory(num_items):
class F(AddEquipmentForm): pass
setattr(F, 'items', FieldList(FormField(EquipmentForm), min_entries=num_items))
return F
num_fields = max(0, request.args.get('cnt', 0, type=int))
form = _factory(num_fields)(request.form)
if form.validate_on_submit():
print(form.items.data)
return render_template('equipment_add.html', **locals())
The template would then look like this for both examples.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title></title>
</head>
<body>
<form>
<label for="cnt">Add Item Count</label>
<input type="number" name="cnt" value="{{ num_fields }}" min="0" />
<input type="submit" />
</form>
<hr/>
{% if form.items | count > 0 -%}
<form method='post'>
{{ form.csrf_token }}
{% for subform in form.items -%}
{% for field in subform -%}
<div class="">
{{ field.label() }}
{{ field() }}
{% if field.errors -%}
<ul>
{% for error in field.errors -%}
<li>{{ error }}</li>
{% endfor -%}
</ul>
{% endif -%}
</div>
{% endfor -%}
{% endfor -%}
{{ form.submit() }}
</form>
{% endif -%}
</body>
</html>
I am trying to have a view function paste decimal values (eventually from a database) into a WTF form field. The value does not show up unless I change the field to a string field. Do I need something extra to paste a numerical value into a decimal field?
I have created a small, hopefully reproducible example to test this functionality only. It has a single field which, upon loading the page, should paste a value in the field.
Originally I tried this with a decimal field, and tried pasting a random number, but that number doesn't show up when the page is rendered. The same happens when the field is changed to an IntegerField or FloatField
When changing the form field to a string field, the value will show up upon page rendering - however this is not an ideal solution since the value will be a string.
routes.py
from flask import render_template, flash
from app import app
from app.forms import TestForm
#app.route('/',methods=['GET','POST'])
#app.route('/index/',methods=['GET','POST'])
def index():
# define form
test_form = TestForm()
test_form.testfield.data = 10
return render_template('sandbox_home.html',
test_form = test_form
)
forms.py
from flask_wtf import FlaskForm
from wtforms import StringField,DecimalField
class TestForm(FlaskForm):
testfield=StringField()
sandbox_home.html
<!doctype html>
<title>My Safe Place</title>
<p>I exist to paste values to form fields!</p>
<form action="" method="post" novalidate>
{{ test_form.hidden_tag() }}
{{ test_form.testfield() }}
</form>
I would expect the value to show up regardless of the field type, if the input is of the correct type. I am guessing this is a datatype issue, but I'm not sure where the issue is.
Here's a minimal example that shows this working OK:
from flask import Flask, render_template_string
from flask_wtf import FlaskForm
from wtforms import StringField, IntegerField, DecimalField
app = Flask(__name__)
app.secret_key = ";lskjflksdjflsdkjfsd;lghdi9uawhj"
class TestForm(FlaskForm):
test_str = StringField()
test_int = IntegerField()
test_dec = DecimalField()
template = """
<!doctype html>
<title>My Safe Place</title>
<p>I exist to paste values to form fields!</p>
<form action="" method="post" novalidate>
{{ test_form.hidden_tag() }}
{{ test_form.test_str() }}
{{ test_form.test_int() }}
{{ test_form.test_dec() }}
</form>
"""
if __name__ == "__main__":
# simulate handling a request
with app.test_request_context():
test_form = TestForm()
test_form.test_str.data = 10
test_form.test_int.data = 10
test_form.test_dec.data = 10
# render the template and save to file so can open in browser
with open("test.html", "w") as f:
f.write(render_template_string(template, test_form=test_form))
It saves the rendered template to a file called test.html which looks like this:
<!doctype html>
<title>My Safe Place</title>
<p>I exist to paste values to form fields!</p>
<form action="" method="post" novalidate>
<input id="csrf_token" name="csrf_token" type="hidden" value="IjUyMDI2NDYxMDY2OTc3N2E0OTU3ODYwM2M0Y2RiMzNjYjY5OWZlMDAi.XcXs_g.55IiutLhjhC2h7J9MI8rWtNu9co">
<input id="test_str" name="test_str" type="text" value="10">
<input id="test_int" name="test_int" type="text" value="10">
<input id="test_dec" name="test_dec" type="text" value="10.00">
</form>
And here is screenshot of that in the browser:
As you can see from the rendered template, WTForms doesn't differentiate between the different types of fields in the browser (i.e., they are all type="text"). If you can let me know how to modify this script to reproduce your error I'm happy to look at it again.
Here is the code snippet I have written
MainPage.py
from flask import Flask, render_template,url_for,request, redirect
from form import SearchForm
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secretkey1234'
#app.route("/")
#app.route("/home", methods=['GET', 'POST'])
def home():
forms = SearchForm()
return render_template('home.html', title='Home', forms=forms)
#app.route("/about")
def about():
return render_template('about.html', title='About')
if __name__ == '__main__':
app.run(debug=True)
form.py
from flask_wtf import FlaskForm
import Get_Test_Suite
from wtforms.fields import SelectField, SubmitField
from wtforms.fields.html5 import DateField
from wtforms.validators import DataRequired
class SearchForm(FlaskForm):
test_suite_list = Get_Test_Suite.get_test_suite()
suite_list = []
for i in test_suite_list:
suite_list.append((i, i))
test_suite_selected = SelectField('Test Suite Name', choices=suite_list)
test_module_list = Get_Test_Suite.get_module_name()
module_list = []
for j in test_module_list:
module_list.append((j, j))
test_module_selected = SelectField('Test Module Name', choices=module_list,validators=[DataRequired()])
date_selected = DateField('Date', format='%m-%d-%Y')
status = SelectField('Status', choices=[('Active', 'Active'), ('Closed', 'Closed')])
submit = SubmitField('Submit')
home.html
{% extends "layouts.html" %}
{% block content %}
<div class = "content-section">
<form method="POST" action="">
{{forms.hidden_tag()}}
<fieldset class="form-group">
<legend class ="border-bottom mb-4">
<center>SEARCH TEST FAILURE STATUS</center>
</legend>
<div class="form-group">
{{forms.test_suite_selected.label(class="form-control-label")}}
{{forms.test_suite_selected(class="form-control form-control-lg")}}
</div>
<div class="form-group">
{{forms.test_module_selected.label(class="form-control-label")}}
{{forms.test_module_selected(class="form-control form-control-lg")}}
</div>
<div class="form-group">
{{forms.date_selected.label(class="form-control-label")}}
{{forms.date_selected(class="form-control form-control-lg")}}
</div>
<div class="form-group">
{{forms.status.label(class="form-control-label")}}
{{forms.status(class="form-control form-control-lg")}}
</div>
</fieldset>
<div class="form-group">
{{forms.submit(class="btn btn-outline-info")}}
</div>
</form>
</div>
{% endblock content %}
I am creating many drop down lists in home.html
How can I get the data that the user selects from each of the drop down list and print it ? I am trying to create a user friendly web application where the user will select the values from a drop down list and the application will fetch the data accordingly from the database.
#app.route("/")
#app.route("/home", methods=['GET', 'POST'])
def home():
forms = SearchForm()
if request.method == 'POST':
print(forms.field_name.data) # <- prints to console.
# to print to web convert to variable and render with jinja2
# return render_template('display.html', field_name=forms.field_name.data)
return render_template('home.html', title='Home', forms=forms)
Have you tried transforming the 'choices' into a dict? You can then get the value for the key form data. Maybe something like this (for your "status" SelectField): value = dict(form.status.choices).get(form.status.data)
I sure that you was find your solution, but for next generation.
You need insert in the select tag the name of the value, and not in options tags.
example:
<select name="sServer">
{% for server in servers %}
<option value="{{server}}">{{server}}</option>
{% endfor %}
</select>
For some reason, form.validate_on_submit() does not return anything.
from flask import Flask
from flask_wtf import FlaskForm
from wtforms import StringField, DecimalField, validators
from flask import render_template
app = Flask(__name__)
app.config.update(dict(
SECRET_KEY="super awesome key"
))
class MyForm(FlaskForm):
name = StringField('Product name', [validators.InputRequired(), validators.Length(min=0, max=30)])
#app.route('/', methods=['GET', 'POST'])
def index():
form = MyForm()
if form.validate_on_submit():
return "Mission accomplished!"
return render_template('submit.html', form=form)
{% macro render_field(field) %}
<dt>{{ field.label }}
<dd>{{ field(**kwargs)|safe }}
{% if field.errors %}
<ul class=errors>
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</dd>
{% endmacro %}
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Add new grocery product</h1>
<p>Provide appropriate product details</p>
<form method="POST" action="/">
{{ form.csrf_token }}
{{ render_field(form.name.label) }} {{ form.name(size=20) }}<br>
<input type="submit" value="Go">
</form>
</body>
</html>
The app itself is supposed to gather user input in specific form and insert this via SQLAlchemy into database. Output is supposed to:
Return "Mission accomplished!" if validation succeeded
Return validation of which field failed and display error msg "This field is required" on the submit.html template
EDIT
Duo some success with the code, I make another edit to the question. It seems that macro isn't displaying error message for appropriate field if the validation fails.
I.E. if Field name is empty, macro should create and display error message ( This Field cannot be empty) by itself.
FINAL EDIT
I managed to find the solution. Submit.html template seems to cause the issue with macro if render_field looks like this:
{{ render_field(form.name.label) }}
instaed of this:
{{ render_field(form.name) }}
#This might help
from flask import Flask
from flask_wtf import FlaskForm
#from wtform import SubmitField
from wtforms import StringField, DecimalField, validators,SubmitField
from flask import render_template
app = Flask(__name__)
app.config.update(dict(
SECRET_KEY="super awesome key"
))
class MyForm(FlaskForm):
you need to make an edit to your validators like this
name = StringField('Product name', validators = [InputRequired(), , Length(min=0, max=30)])
#create submit option in your flask form
submit = SubmitField("Submit")
#app.route('/', methods=['GET', 'POST'])
def index():
form = MyForm()
if form.validate_on_submit():
return "Mission accomplished!"
return render_template('submit.html', form=form)
I need to prepopulate form fields with database (dataset) values.
The problem is that I don't know how to send the argument to the form class.
forms.py
# coding: utf-8
from db import produtosalca as produtos ##dataset imports db['table']
from flask_wtf import FlaskForm
from wtforms import TextField, BooleanField, SubmitField, TextAreaField, validators, ValidationError
class ProductForm(FlaskForm,produto):
descricao = TextField("Nome", default=produto.Descricao)
classificacaoFiscal = TextField("NCM", default=produto.ClassificacaoFiscal)
mva = TextField("MVA",default=produto.MVA)
app.py
# coding: utf-8
import os
from werkzeug import secure_filename
from flask import (
Flask, request, send_from_directory, render_template, current_app, flash
)
from db import produtosalca
from forms import ContactForm, ProductForm
app = Flask("alcax")
PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__))
app.config['MEDIA_ROOT'] = os.path.join(PROJECT_ROOT, 'media_files')
app.secret_key = 'development key'
#app.route('/productform', methods=['GET','POST'])
def productform(product):
form = ProductForm(request.form,product) ## here i was gonna send product
if request.method == 'POST':
'posted'
elif request.method == 'GET':
return render_template('productform.html', form=form.content())
Well everything I tried always get me the error
'produto' is not defined
I'm a beginner in py. Have been researching all night long. Thanks in advance.
Note that class ProductForm(FlaskForm) means ProductForm inherets from FlaskForm. A simple solution for your case would be as follows:
forms.py:
from flask_wtf import FlaskForm
from wtforms import TextField
class ProductForm(FlaskForm):
descricao = TextField("Nome")
classificacaoFiscal = TextField("NCM")
mva = TextField("MVA")
app.py:
from flask import Flask, render_template, redirect, request
from forms import ProductForm
app = Flask(__name__)
app.secret_key = 'development key'
#app.route('/productform', methods=['GET','POST'])
def productform():
form = ProductForm(descricao='default_descricao', classificacaoFiscal='default_classificacaoFiscal', mva='default_mva')
if form.validate_on_submit():
form = ProductForm(request.form)
# Do something with your form and redirect the user to some page.
return render_template('productform.html', form=form)
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)
templates/_formhelpers.html
{% macro render_field(field) %}
<dt>{{ field.label }}
<dd>{{ field(**kwargs)|safe }}
{% if field.errors %}
<ul class=errors>
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</dd>
{% endmacro %}
templates/productform.html
<html !DOCTYPE>
<head>
<title>Flask WTF</title>
</head>
<body>
{% from "_formhelpers.html" import render_field %}
<form method="POST" action="/submit">
<dl>
{{ form.hidden_tag() }}
{{ render_field(form.descricao, size=20) }}
{{ render_field(form.classificacaoFiscal, size=20) }}
{{ render_field(form.mva, size=20) }}
</dl>
<input type="submit" value="Go">
</form>
</body>
</html>
Edit to take into account the new queries in the comments below:
forms.py:
class ProductForm(FlaskForm):
descricao = TextField("Nome")
classificacaoFiscal = TextField("NCM")
mva = TextField("MVA")
# ...
def __init__(self, *args, **kwargs):
super(ProductForm, self).__init__(*args, **kwargs)
if 'product' in kwargs:
self.descricao.data = kwargs['product'][0]
self.classificacaoFiscal.data = kwargs['product'][1]
self.mva.data = kwargs['product'][2]
# ...
app.py:
#app.route('/productform', methods=['GET','POST'])
def productform():
# product to be retrieved from your database
form = ProductForm(product=product)
if form.validate_on_submit():
form = ProductForm(request.form)
# Do something with your form and redirect the user to some page.
return render_template('productform.html', form=form)