Flask Form doesn't render in extended template - forms

Created a basic sign in form in flask. The index page renders just fine, however /login does not render any form. No errors have popped up either. [returns a 200 response]
My forms.py is:
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired
class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
password = PasswordField('Password', validators=[DataRequired()])
remember_me = BooleanField('Remember Me')
submit = SubmitField('Sign In')
My routes.py is:
from flask import render_template, flash, redirect, url_for
from app import app
from app.forms import LoginForm
#app.route('/')
#app.route('/index')
def index():
return render_template('index.html', title='Home')
#app.route('/login')
def login():
form = LoginForm()
#return render_template('index.html', title='Home')
return render_template('login.html', title='Sign In', form=form)
My template for the form is:
{% extends "base.html" %}
{% block content %}
<h1>Sign In</h1>
<form action="" method="post" novalidate>
<!---{{ form.hidden_tag() }}-->
<p>
{{ form.username.label }}<br>
{{ form.username(size=32) }}
</p>
<p>
{{ form.password.label }}<br>
{{ form.password(size=32) }}
</p>
<p>{{ form.remember_me() }} {{ form.remember_me.label }}</p>
<p>{{ form.submit() }}</p>
</form>
{% endblock %}
The index base that the template above extends is rendered but no form:
The terminal output:
Tried this on chrome, safari and firefox.

You probably forgot to declare your block in the base.html file.
In base.html :
<body>
... base content ...
{% block body %}
... your login.htlm will set there ...
{% endblock %}
</body>

Related

Flask-WT Form: How to pass additional parameters for form

In a jinja template I want to have a class for the form:
{% macro build_form(form) %}
<form class="{{ form.form_class }}" role="form" method="post" action="" class="text-start">
{{ form.hidden_tag() }}
{% for field in form if field.widget.input_type != 'hidden' %}
<div class="{{ field.render_kw.container2_class }}">
<div class="{{ field.render_kw.container1_class }}">
<label class="form-label"> {{ field.label }}</label>
{{ field}}
</div>
...
I want to pass it as a parameter of the form, something like
user_form = UserForm(obj=user, form_class="row")
However, it's not recognized in the template. How and where can I send such parameters.
Easiest would be to create a custom form where you can pass the class name as a string keyword kwargs and then use this form as the base class for all your forms that need to support said class string. For example:
class CustomForm(Form):
def __init__(self, formdata=None, obj=None, prefix="", data=None, meta=None, **kwargs):
self.form_class = kwargs.pop('form_class', None)
super(CustomForm, self).__init__(formdata, obj, prefix, data, meta, **kwargs)
Example in use:
from flask import Flask, render_template_string
from wtforms import Form, StringField
app = Flask(__name__)
class CustomForm(Form):
def __init__(self, formdata=None, obj=None, prefix="", data=None, meta=None, **kwargs):
self.form_class = kwargs.pop('form_class', None)
super(CustomForm, self).__init__(formdata, obj, prefix, data, meta, **kwargs)
class TestForm(CustomForm):
first_name = StringField()
html_template = '''
<form class="{{ form.form_class }}" role="form" method="post" action="">
{{ form.first_name }}
</form>
'''
#app.route('/')
def custom_form():
_form = TestForm(form_class='form-class')
return render_template_string(html_template, form=_form)
if __name__ == '__main__':
app.run()

Can't get error message to show with WTForms _formhelpers to show on Flask webpage

Apologies if this isn't worded very well. I can't seem to get any error messages to show when I haven't fulfilled the validation requirements. When I submit, the form is just refreshed without error messages such as "This field is required".
Also, when I do input data, if form.validate_on_submit(): doesn't return the expected homepage, it once again just refreshes the page. I'm aware the form does nothing, I plan on adding the data submitted to a database but I'm stuck on this step. Can anybody help? Thanks.
app.py
from cs50 import SQL
from flask import Flask, render_template, session, redirect
from flask_session import Session
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField #bool for checkbox
from wtforms.validators import InputRequired, Email, Length
# configure application
app = Flask(__name__)
app.config["SESSION_PERMENANT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)
# Allows webpage to update html on refresh
app.config["TEMPLATES_AUTO_RELOAD"] = True
app.config['SECRET_KEY'] = 'Itsasecret'
# configure CS50 library to use SQLite database
db = SQL("sqlite:///project.db")
# Usually separate routes, database and form stuff into different files and link together
class LoginForm(FlaskForm):
username = StringField('Username', validators=[InputRequired(), Length(min=4, max=15)])
password = PasswordField('Password', validators=[InputRequired(), Length(min=8, max=80)]) #80 is a special number (learn more)
remember = BooleanField('Remember me')
#app.route('/')
def homepage():
return render_template("homepage.html")
#app.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
return redirect("/")
return render_template("login.html", form=form)
login template
{% extends "layout.html" %}
{% from "_formhelpers.html" import render_field %}
{% set active_page = 'login' %}
{% block title %} Login {% endblock %}
{% block body %}
<div class="ls-main">
<form action="/login" method="POST">
{{ form.csrf_token }}
{{ render_field(form.username) }}
{{ render_field(form.password, autocomplete="off") }}
{{ render_field(form.remember) }}
<div class="ls-div">
<input class="ls-input, signinbutton" name="submit" type="submit" value="Submit">
</div>
</form>
</div>
{% endblock %}
_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 %}
return render_template("/") is invalid. I think you mean return redirect("/")
Of course you need from flask import Flask, render_template, session, redirect

Flask form routing

I'm struggling with a Flask App that gives me Bad Request on a form submission. I've been through the little bit of documentation from Flask and a number of SO pages. I'm sure there's something simple I'm missing.
In a nutshell, I've developed a template to receive the form called 'placeindex.html'. The form 'name' data all matches. If I change the name of 'placeindex.html' to 'index.html' it works fine, even though I'm pointing to the 'placeindex.html' file. Code below (view):
#app.route('/add_place', methods=['GET', 'POST'])
def add_place():
username = session['username']
placename = request.form['place']
address = request.form['address']
city = request.form['city']
state = request.form['state']
zipcode = request.form['zipcode']
alias = request.form['alias']
searchword = request.args.get('key', '')
print(searchword)
Place(session['username']).new_place(placename, address, city, state, zipcode, alias, username)
return render_template('placeindex.html')
placeindex.html:
{% extends "layout.html" %}
{% block place %}
<h2>Home</h2>
{% if session.username %}
<h3>Add new 'placeindex'</h3>
<form action="{{ url_for('add_place') }}" method="post">
<dl>
<dt>Name:</dt>
<dd><input type="text" size="30" name="place"></dd>
<dt>Address:</dt>
<dd><input type="text" size="30" name="address"></dd>
<dt>City:</dt>
<dd><input type="text" size="30" name="city"></dd>
<dt>State:</dt>
<dd><input type="text" size="2" name="state"></dd>
<dt>Zip Code:</dt>
<dd><input type="text" size="10" name="zipcode"></dd>
<dt>Nickname:</dt>
<dd><input type="text" size="30" name="alias"></dd>
</dl>
<input type="submit" value="Save">
</form>
{% endif %}
<br>
<h3>My Places</h3>
{% include "display_posts.html" %}
{% endblock %}
I've stripped out all the validation code to try to figure this out.
layout.html (in case it helps):
<!doctype html>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">
<div class="page">
<div class="metanav">
{% if session.username %}
Logged in as {{ session.username }}
{% endif %}
Home
{% if not session.username %}
Register
Login
{% else %}
Profile
Logout
Places
Trips
Delegates
{% endif %}
</div>
{% for message in get_flashed_messages() %}
<div class="flash">{{ message }}</div>
{% endfor %}
{% block body %}{% endblock %}
{% block post %}{% endblock %}
{% block place %}{% endblock %}
{% block trip %}{% endblock %}
</div>
Once I open the 'index.html' version of the file and send form data, it refreshes to the correct file, but I can't go there directly without the BAD REQUEST page.
Thanks
The issue is that you are accessing request.form without checking if request.form is populated. form contains arguments in the body of the HTTP request ... and GET requests don't have a body.
As discussed in Form sending error, Flask - when Flask is asked for a form variable that doesn't exist it throws an error ... just like an ordinary Python dictionary does.
The solution in your case is to wrap all of your data-processing code inside of a conditional:
if request.method == "POST":
# code accessing request.form here
So the general structure is:
#app.route('/add_place', methods=['GET', 'POST'])
def add_place():
if request.method == "POST":
username = session['username']
placename = request.form['place']
# ... snip ...
# Note that this is not under the `if`
# so we'll *always* return this template's HTML.
return render_template('placeindex.html')

Symfony 2 custom form field type: how to add javascript and css only once?

I want to use javascript in custom Symfony 2 form field type extension. So, I have Twig extension template like this:
{% block some_widget %}
<input ... />
<script src="some.js"></script>
<link href="some.css" />
{% endblock %}
But I want to have these script and link tags only once in my HTML, ideally in head tag, without modifing base template. I tried to extend Twig blocks, but I have no access to action template blocks inside form template. Or maybe something like this:
{# widget tempate #}
{% block some_widget %}
<input ... />
{{ use_javascript('some.js') }}
{{ use_css('some.css') }}
{% endblock %}
{# main action template #}
...
<head>
{{ dump_javascripts() }}
{{ dump_css() }}
</head>
...
How to do this with Symfony 2 Forms + Twig?
P.S. Sorry for my bad english.
I had to write a self contained form widget that requires javascript, I was able to achieve what you are trying to do through the event_dispatcher listening on the kernel.response to append the javascript at the end of the Symfony\Component\HttpFoundation\Response. Here's a snippet of my form type :
<?php
namespace AcmeBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface;
class AcmeFileType extends AbstractType{
private $twig;
private $dispatcher;
public function __construct(\Twig_Environment $twig, EventDispatcherInterface $dispatcher){
$this->twig = $twig;
$this->dispatcher = $dispatcher;
}
public function buildView(FormView $view, FormInterface $form, array $options){
$javascriptContent = $this->twig->render('AcmeBundle:Form:AcmeFileType.js.twig', array());
$this->dispatcher->addListener('kernel.response', function($event) use ($javascriptContent) {
$response = $event->getResponse();
$content = $response->getContent();
// finding position of </body> tag to add content before the end of the tag
$pos = strripos($content, '</body>');
$content = substr($content, 0, $pos).$javascriptContent.substr($content, $pos);
$response->setContent($content);
$event->setResponse($response);
});
}
...
When you define your form type in your services.yml it looks like this :
acme.form.acme_file_type:
class: AcmeBundle\Form\AcmeFileType
arguments:
- #twig
- #event_dispatcher
tags:
- { name: form.type, alias: acmefile }
So now, everytime you build a form with acmefile the javascript will be appended to the <body>. This solution does not prevent the javascript from being present multiple time though, but you should easily be able to improve this to suit your needs.
You can also play around with the $response object to modify the headers instead if you wish.
The best way is to provide the separate template with css & scripts loading.
With the comments in readme so only thing developer will must do is to
{% block stylesheets %}
{{ parent() }}
include "#MyBestBundle/Resources/view/styles.html.twig"
{% endblock %}
or try to intercept the form rendering with DI and add the assets. But it more difficult to do , if possible to implement.
My way of doing was by creating a custom twig extension where I add JS to a buffer and during form rendering and later dump it at the of my layout.
Something like this:
<?php
namespace AppBundle\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
class WysiwygExtension extends AbstractExtension
{
/**
* #var array
*
* A pool of elements IDs for Wysiwyg binding.
*/
private $wysiwygElements = [];
/**
* {#inheritdoc}
*/
public function getFunctions()
{
return array(
new TwigFunction('addWysiwygBinding', [$this, 'addWysiwygBinding']),
new TwigFunction('popWysiwygBindings', [$this, 'popWysiwygBindings']),
);
}
public function addWysiwygBinding(string $id): void
{
$this->wysiwyglements[] = $id;
}
public function popWysiwygBindings(): array
{
$elements = array_unique($this->wysiwygElements);
$this->wysiwygElements = [];
return $elements;
}
}
Then form-fields.html.twig:
...
{% block wysiwyg_widget %}
{% apply spaceless %}
{{ form_widget(form) }}
{% do addWysiwygBinding(id) %}
{% endapply %}
{% endblock %}
...
Then layout.html.twig:
<!DOCTYPE html>
<html>
<head>
...
</head>
<body>
...
{% set ids = popWysiwygBindings() %}
{% if ids is not empty %}
{% javascripts
'bundles/admin/plugins/wysiwyg_1.js'
'bundles/admin/plugins/wysiwyg_2.js'
%}
<script type="text/javascript" src="{{ asset_url }}"></script>
{% endjavascripts %}
{% endif %}
{% for id in ids %}
{{ include('_wysiwyg.html.twig', { id: id }) }}
{% endfor %}
</body>
</html>
This is how I use it. Hope it's what you're looking for.
base.html.twig
<head>
{% block stylesheets %}
css...
{% endblock %}
</head>
foo.html.twig
{% extends '::base.html.twig' %}
{% block stylesheets %}
{{ parent() }}
css that you need in foo.html.twig
{% endblock %}
I found some 'dirty' method used by many peoples in other situations.
We check the loading of script on client side. In case we have a zlkladr.js file, that have a global object 'zlkladr'
{% block our_widget %}
{% spaceless %}
...
<script>
// We must load the script only once, even if many widgets on form
if ( !window.zlkladr ) {
document.write('<script src="{{ asset('bundles/kladr/js/zlkladr.js') }}"></sc'+'ript>');
}
</script>
{% endspaceless %}
{% endblock %}

Rendering a Form in a Twig Template with Symfony2

I have a base Twig template that has a search bar form in it at the top of the page in a Twig block. I have another block later on named "content" that my children pages fill out. Currently, my base template looks like this:
{% block admin_bar %}
<div id="search">
<form action="{{ path('search') }}" method="post" {{ form_enctype(search_form) }}>
{{ form_widget(search_form.term) }}
{{ form_widget(search_form.type) }}
{{ form_widget(search_form.pool) }}
{{ form_widget(search_form._token) }}
<input type="submit" value="Search" />
</form>
</div>
{% endblock %}
{% block content %}
{% endblock %}
However, when trying to render a child template I need to pass in the search_form variable along with it. Is there anyway (short of writing out the HTML tags myself) I can avoid having to create this search_form variable and pass it in everytime I want to render a child view? I'm using Twig in conjunction with Symfony2.
Thanks!
Embedded Controller is what you need. Put your admin_bar block into separate file:
{# src/Acme/AcmeBundle/Resources/views/Search/index.html.twig #}
<div id="search">
<form action="{{ path('search') }}" method="post" {{ form_enctype(search_form) }}>
{{ form_widget(search_form.term) }}
{{ form_widget(search_form.type) }}
{{ form_widget(search_form.pool) }}
{{ form_widget(search_form._token) }}
<input type="submit" value="Search" />
</form>
</div>
Create controller for this template:
class SearchController extends Controller
{
public function indexAction()
{
// build the search_form
return $this->render('AcmeAcmeBundle:Search:index.html.twig', array('search_form' => $searchForm));
}
}
And then embed controller into your original template:
{% block admin_bar %}
{% render "AcmeAcmeBundle:search:index" %}
{% endblock %}
{% block content %}
{% endblock %}