Jinja Template Code
<form method="POST">
{{ form.name.label }} {{ form.name() }}
{{ form.submit() }}
</form>
Form Class
class NameForm(FlaskForm):
name = StringField('What is your name?', validators=[Required()])
submit = SubmitField('Submit')
pdb> request.form
ImmutableMultiDict([('name', 'rohit'), ('submit', 'Submit')])
form.validate_on_submit() is returning False instead of True.
Fr the above to work you also need to add protection against cross-site referencing in the HTML code. Adding this line
{{ form.hidden_tag() }}
might solve the problem.
Related
I am writing an application on Flask, I made authorization on flask-login, but an error appears during authorization itself. I use also Flask-WTF.
I have the simplest form of Flask WTF:
class LoginForm(FlaskForm):
email = StringField("Email: ", validators=[Email()])
password = PasswordField("Password: ", validators=[DataRequired(), Length(min=4, max=100)])
submit = SubmitField("Войти")
There is an HTML form:
<form action="/accounting" method="POST">
{{ form.csrf_token }}
{{ form.email.label() }} {{ form.email() }}
{{ form.password.label() }} {{ form.password() }}
{{ form.submit() }}
</form>
I tried to specify the HTML form {{ form.hidden_tag() }}
There is also a function:
#app.route('/login', methods=['POST', 'GET'])
def login():
if current_user.is_authenticated:
return redirect('/accounting')
form = LoginForm()
if form.validate_on_submit():
email = request.form['email']
user = UserModel.query.filter_by(email=email).first()
if user is not None and user.check_password(request.form['password']):
login_user(user)
return redirect('/accounting')
return render_template('login.html', form=form)
I couldn't find an answer on the Internet that fits my case, so I'm asking here.
Why does an error appear when submitting the form?
Bad Request
The CSRF session token is missing.
After rendering, the token is present:
<input id="csrf_token" name="csrf_token" type="hidden" value="IjhlMDBjMDhmYTIwOGUyNGQ5OGNiMTY0ZGZhOTU3Njc0ZDJhNjY4MDgi.YSW1_g.fhKQUYljjLKqUtl0OdcuOgJx02U">
I tried it both ways:
app.config['SECRET_KEY'] = SECRET_KEY
app.secret_key = "ff82b98ef8727e388ea8bff063"
There are also such lines:
csrf = CSRFProtect(app)
csrf.init_app(app)
Your error is in the html template. remove the action and add the hidden tags
<form method="POST">
{{ form.hidden_tag() }}
{{ form.email.label() }} {{ form.email() }}
{{ form.password.label() }} {{ form.password() }}
{{ form.submit() }}
</form>
I have a macro that's defined like this:
{% macro render_row (class_suffix, form, field) %}
<div class="form-{{ class_suffix }}__row">{{ form.field.label }} {{ form.field() }}</div>
{% endmacro %}
I want to pass a form object and a field parameter to render a specific row, like this:
{% import "macros.html" as macros %}
...
<div class="form-container">
<h2>Sign In</h2>
<form class="form-login" action="{{ url_for('signin') }}" method="post">
{{ macros.render_row ('login', form, email) }}
{{ macros.render_row ('login', form, password) }}
{{ macros.render_submit ('login', 'Sign In') }}
{{ form.csrf_token }}
</form>
</div>
For example, macros.render_row ('login', form, email) should be expanded to:
<div class="form-login__row">{{ form.email.label }} {{ form.email() }}</div>
Instead, I get this error:
jinja2.exceptions.UndefinedError: 'blog.forms.SignInForm object' has no attribute 'field'
Form's defined like this:
class SignInForm(FlaskForm):
email = StringField('Email', validators=[DataRequired(), Email(), Length(max=64)])
password = PasswordField('Password', validators=[DataRequired(), Length(min=8, max=64)])
I've also found a solution, but I'm curious why previous method doesn't work.
{% macro render_row (class_suffix, form_field) %}
<div class="form-{{ class_suffix }}__row">{{ form_field.label }} {{ form_field() }}</div>
{% endmacro %}
...
{{ macros.render_row ('login', form.email) }}
Is it possible to do? What am I doing wrong? Is it even a good practice to do something like this?
That's because Jinja2 tries to get the property named field from the form object passed, not the field which name equals to the variable field passed.
It tries to do
{{ foo.field }}
{{ foo['field'] }}
But for your case, it will work only using this syntax, of course.
{{ foo[field] }}
Where field param for the macros should be a string I believe, now I think that's None.
So it should be
{{ macros.render_row ('login', form, 'email') }}
and the definition of the macro like
{% macro render_row (class_suffix, form, field) %}
<div class="form-{{ class_suffix }}__row">{{ form[field].label }} {{ form[field]() }}</div>
{% endmacro %}
Check more details on the variable's syntax page https://jinja.palletsprojects.com/en/2.10.x/templates/#variables.
I haven't tested it in real files, not have Jinja2 installed in my hands. But it should be working like that.
I am building a form in my Flask app and want the users to be able to select an option from a <select> menu.
For example:
<select>
<option>Banana</option>
<option>Pineapple</option>
</select>
My current "HTML" for this is
<form action="{{ url_for('contact') }}" method=post>
{{ form.hidden_tag() }}
{{ form.name.label }}
{{ form.name }}
{{ form.company.label }}
{{ form.company }}
{{ form.email.label }}
{{ form.email }}
{{ form.phone.label }}
{{ form.phone }}
{{ form.message.label }}
{{ form.message }}
{{ form.submit }}
</form>
My Python Class for this form is:
class ContactForm(Form):
partner_type = SelectField(
u'Industry Type',
choices = [('Software', 'software'), ('Sales', 'sales')]
)
name = TextField("Name", [validators.Required("Please enter your full name.")])
email = TextField("Email", [validators.Required("Please enter a valid email i.e. you#yourdomain.com"), validators.Email()])
phone = TextField("Phone", [validators.Required("Please enter a valid phone number.")])
company = TextField("Company", [validators.Required("Please enter your company name.")])
message = TextAreaField("Message", [validators.Required("Please include a message regarding this submission.")])
submit = SubmitField("Send")
This all works great for text fields and submit button but I can't seem to figure out how to properly render the <select> options to the form. I tried following the syntax above by doing:
{{ form.industry_type.label }}
{{ form.industry_type }}
but that didn't work. It just added the label and no drop down selector. Is there a proper way to add this to the DOM? The options are static, preset options, so there's nothing dynamic or complex about the list I am trying to render.
Thanks for the help. I am very new to Python so I am still figuring a lot of things out.
You should be using:
{{ form.partner_type.label }}
{{ form.partner_type }}
instead of:
{{ form.industry_type.label }}
{{ form.industry_type }}
The field name in the form is the variable name it is assigned to, not the label in the constructor of the SelectField.
I made a flask_wtf Form with this field:
logo_image = FileField('logo_image', validators=[FileRequired(), FileAllowed(['jpg', 'png'], 'Images only!')])
My form looks like this:
<form action="" method="POST" name="app_branding" enctype="multipart/form-data">
{{ form.csrf_token }}
{{ form.brand.label }} {{ form.brand }}
{{ form.logo_image.label }} {{ form.logo_image }}
{{ form.title_text.label }} {{ form.title_text }}
{{ form.first_paragraph.label }} {{ form.first_paragraph }}
{{ form.faq.label }} {{ form.faq }}
{{ form.privacy_policy.label }} {{ form.privacy_policy }}
{{ form.success_message.label }} {{ form.success_message }}
{{ form.submit.label }} {{ form.submit }}
</form>
For debugging, in my view, I put:
#expose('/', methods=['GET', 'POST'])
def index(self):
form = BrandForm(request.form)
print(form.validate())
print(form.errors)
print("request.files")
print(request.files)
And in the console I get the message that logo_image is required, even though it is there in request.files:
False
{'logo_image': ['This field is required.']}
request.files
ImmutableMultiDict([('logo_image', <FileStorage: u'20140725_095232.jpg' ('image/jpeg')>)])
How do I get the FileRequired() method to detect the file?
request.form only contains form input data. request.files contains file upload data. You need to pass the combination of both to the form. Since your form inherits from Flask-WTF's Form (now called FlaskForm), it will handle this automatically if you don't pass anything to the form.
form = BrandForm()
if form.validate_on_submit():
...
Without Flask-WTF, use a CombinedMultiDict to combine the data and pass that to the form.
from werkzeug.datastructures import CombinedMultiDict
form = BrandForm(CombinedMultiDict((request.files, request.form)))
if request.method == 'POST' and form.validate():
...
I made a flask_wtf Form with this field:
logo_image = FileField('logo_image', validators=[FileRequired(), FileAllowed(['jpg', 'png'], 'Images only!')])
My form looks like this:
<form action="" method="POST" name="app_branding" enctype="multipart/form-data">
{{ form.csrf_token }}
{{ form.brand.label }} {{ form.brand }}
{{ form.logo_image.label }} {{ form.logo_image }}
{{ form.title_text.label }} {{ form.title_text }}
{{ form.first_paragraph.label }} {{ form.first_paragraph }}
{{ form.faq.label }} {{ form.faq }}
{{ form.privacy_policy.label }} {{ form.privacy_policy }}
{{ form.success_message.label }} {{ form.success_message }}
{{ form.submit.label }} {{ form.submit }}
</form>
For debugging, in my view, I put:
#expose('/', methods=['GET', 'POST'])
def index(self):
form = BrandForm(request.form)
print(form.validate())
print(form.errors)
print("request.files")
print(request.files)
And in the console I get the message that logo_image is required, even though it is there in request.files:
False
{'logo_image': ['This field is required.']}
request.files
ImmutableMultiDict([('logo_image', <FileStorage: u'20140725_095232.jpg' ('image/jpeg')>)])
How do I get the FileRequired() method to detect the file?
request.form only contains form input data. request.files contains file upload data. You need to pass the combination of both to the form. Since your form inherits from Flask-WTF's Form (now called FlaskForm), it will handle this automatically if you don't pass anything to the form.
form = BrandForm()
if form.validate_on_submit():
...
Without Flask-WTF, use a CombinedMultiDict to combine the data and pass that to the form.
from werkzeug.datastructures import CombinedMultiDict
form = BrandForm(CombinedMultiDict((request.files, request.form)))
if request.method == 'POST' and form.validate():
...