Getting werkzeug.routing.BuildError - python

I'm getting a werkzeug.routing.BuildError when I go to my "login.html" page. The problem seems to be the "action=" attribute in the template. Originally it was action={{url_for('login')}}. Although the docs show it done this way it doesn't seem to work. When I change it to action="/login" or action="#" it works correctly. The question is why? I was under the impression that the correct way was the action={{url_for('login')}}?
Before I broke my code up into packages (everything in single py file) it worked correctly.
BTW, most of this code is from Miguel Grindberg's GREAT book "Flask Web Development". The code I'm having problems with is my own that I added going through the book. I'm on WinXP and using the most up to date Flask. Here is my code below:
flasky\app\main\views.py:
from flask import render_template, session, redirect, url_for, current_app, flash
from .. import db
from ..models import User
from ..email import send_email, post_mail
from . import main
from .forms import NameForm, RegForm
#main.route('/login', methods=['GET', 'POST'])
def login():
form = RegForm()
if form.validate_on_submit():
session['name'] = form.username.data
session['logged_in'] = True
return redirect(url_for('success'))
return render_template('login.html', form=form)
flasky\app\templates\login.html:
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Sign Up{% endblock %}
{% block content %}
{{ super() }}
<div class="well">
<h1 align="center">Sign-In</h1>
</div>
<div class="container">
<form class="form form-horizontal" action="{{url_for('login')}}" method="post">
{{form.hidden_tag()}}
{{wtf.form_field(form.username)}}
{{wtf.form_field(form.email)}}
{{wtf.form_field(form.password)}}
{{wtf.form_field(form.bool)}}
{{wtf.form_field(form.submit)}}
</form>
</div>
{% endblock %}
<!-- action= {{url_for('login')}} doesn't work. . ."#" and "\login" work-->
flasky\app\main\forms.py:
from flask.ext.wtf import Form
from wtforms import StringField, SubmitField, PasswordField, BooleanField, SubmitField
from wtforms.validators import Required, Email
class RegForm(Form):
username = StringField('Username', validators=[Required()])
email = StringField('Email Address', validators=[Email()])
password = PasswordField('Password', validators=[Required()])
bool = BooleanField("I Agree To Your Terms of Services", validators=[Required()])
submit = SubmitField('Submit')

A BuildError is raised when the url_for() method cannot locate an endpoint that fits the description. In this case, no login endpoint was found to be registered with the Flask app object.
You appear to have registered the login route with a Blueprint named main instead; you need to use the blueprint name in the endpoint name:
{{ url_for('main.login') }}
If the template is only ever used by routes in the main blueprint, you can make it relative too by starting the name with a .:
{{ url_for('.login') }}
See the Building URLs section of the Blueprints documentation.

Related

Why am I getting the error: "jinja2.exceptions.UndefinedError: 'form' is undefined" on flask/python/html project?

I'm creating a flask/python/html project that asks the user for their name. I'm trying to use wtforms to validate the input.
This is the html (mainScraper.html):
{% extends 'base.html' %}
{% block head %}
<link rel ="stylesheet" href = "mainScraper.css">
{% endblock %}
{% block body %}
<h1> This is the main page </h1>
<p> Please enter your name: </p>
<br/>
<form action = "mainScraper", method = "POST">
{{ form.csrf_token }}
{{ form.username.label }}
{{ form.username }}
{{ form.submit }}
</form>
{% endblock %}
And this is the flask/python:
from flask import Flask, render_template, request, redirect, url_for, session, flash
from flask_wtf import FlaskForm
from wtforms import SubmitField, StringField
from wtforms.validators import data_required, length
app = Flask(__name__)
app.secret_key = "34Secret56"
#app.route('/')
def index():
return render_template('mainScraper.html')
name = ' '
class NameForm(FlaskForm):
username = StringField('Name', validators = [data_required(),length(min = 1)])
submit = SubmitField('Submit')
#app.route('/mainScraper', methods = ['POST', 'GET'])
def mainScraper():
form = NameForm()
if form.validate_on_submit():
global name
name = form.username.data
return render_template('general.html')
return render_template('mainScraper.html', form=form)
I want to make sure at least one character is submitted before moving on to the next html page (from the mainScraper.html to the general.html).
But I'm getting this error from my mainScraper.html file: "jinja2.exceptions.UndefinedError: 'form' is undefined" and I'm confused as to why, because I defined 'form' in the corresponding method of the python file, mainScraper. And I’m not sure how else I should validate my forms in html because most online videos or websites I’ve seen use flask-WTF or WTForms with {{ form.csrf_token}} in a html file
Also I’m not using an IDE for the python file, but React for the html file, is that a problem?
If so, what IDE do you suggest I use for the python and html file?

How to take form data from Flask template and pass to HTML page [duplicate]

This question already has answers here:
How to pass a variable between Flask pages?
(2 answers)
Closed 9 months ago.
Hello everyone I'm new to Flask and I cant wrap my mind around this concept for some reason. What I essentially have so far is a user registration page. In my flaskschool.py I'm creating a form called the RegistrationForm. I'm then creating a template for that form in my register.html. In my view function I'm then rendering that template and printing out the value for User I took in from the form out to my HTML page. My goal is to present the user with the form they just used as well as Welcome message with whatever value they just entered for username in the form. An advice helps, Thanks!
flapp.py
from flask import Flask, redirect, url_for, render_template, request, session, flash
from flask_wtf import FlaskForm
from flaskschool import DateForm, RegistrationForm
from upload import UploadForm
app = Flask(__name__)
# for decrypting session data
app.secret_key = "testb"
#app.route("/")
def home():
return render_template("index.html")
## test site for user registration/
#app.route('/register', methods=['GET', 'POST'])
def register():
form = RegistrationForm()
if form.validate_on_submit():
user = form.username.data
email = form.email.data
return render_template('register.html', form=form, user=user)
if __name__ == "__main__":
app.run(debug=True)
flaskschool.py
from wtforms import Form, BooleanField, StringField, PasswordField, validators
from wtforms.fields.html5 import DateField, DateTimeField
from flask_wtf import FlaskForm
from datetime import date
## playing with custom validators
def validate_username(form, field):
if len(field.data) > 25:
raise validator.ValidationError("Username must be less than 25 characters")
class RegistrationForm(FlaskForm):
username = StringField('Username', [validate_username])
email = StringField('Email Address', [validators.Length(min=6, max=35)])
password = PasswordField('New Password', [validators.DataRequired(),
validators.EqualTo('confirm', message='Passwords must match')])
confirm = PasswordField('Repeat Password')
accept_tos = BooleanField('I accept the TOS', [validators.DataRequired()])
enter code here
register.html
{% from "_formhelpers.html" import render_field %}
<form method="POST" action="/register">
<dl>
</dl>
{{ render_field(form.username) }}
{{ render_field(form.email) }}
{{ render_field(form.password) }}
{{ render_field(form.confirm) }}
{{ render_field(form.accept_tos) }}
</dl>
<p><input type=submit value=submit>
</div>
</form>
<br>
<h1>Welcome: {{user}}</h1>
<br>
If you want to retrieve a registered user's information, it will be necessary for you to work with a database. You will be able to query the database to retrieve a user's information, and present it in a template.
You have a lot going on well for you above except for some minor modifications that you will need to do, and a few things you can add. I have started from the very beginning so you can follow through:
Create a simple application to ensure that every part of flask is working
Configure your application to work with web forms and a database
Create a simple database (I will use sqlite. You can use any other if you want)
Create registration and login form structures
Display the forms in HTML templates
Render those templates using view functions
Consider using the structure below when building your application. It is convinient to follow the principle of separation of concerns when building your application in that parts of your application are clustered in modules.
project
| --- flapp.py
| --- config.py
| --- .flaskenv
| --- app/
| --- __init__.py
| --- routes.py
| --- models.py
| --- forms.py
| --- templates/
| --- base.html
| --- home.html
| --- register.html
| --- login.html
| --- static/
1. Create an application instance and ensure the initial project is working
Install flask, flask-wtf, flask-login, email-validator, python-dotev in your virtual environment
Initialize these packages in __init__.py file:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
from config import Config
app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)
migrate = Migrate(app, db)
login = LoginManager(app)
login.login_view = 'login'
from app import routes, models
I have added login.login_view = 'login' which will be used to protect the Home page from being accessed by anonymous users. I have also imported the Config class from the config module which we will create in a section below.
# flapp.py
from app import app
This file acts as an entry point to your flask application.
# routes.py
from app import app
#app.route('/')
#app.route('/home')
def home:
return 'home'
The home() view function will return the text "home" when you run the application.
# .flaskenv
FLASK_APP=school.py
FLASK_ENV=development
FLASK_DEBUG=True
Flask expects these variables before starting the server. The package python-dotenv installed earlier is used to load them instead of having to pass each variable in the terminal before typing flask run.
When you run the application, you should see "Home".
2. Configure the application
Install flask-sqlalchemy, flask-migrate. They are needed to run and manage your database. Respective variables are initialized from os.environ.get.
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config(object):
SECRET_KEY = os.environ.get('SECRET_KEY') or 'difficult-to-guess'
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'app.db')
SQLALCHEMY_TRACK_MODIFICATIONS = False
Feel free to change the database to whichever you want. SECRET_KEY is needed when you work with web forms.
3. Create a database
# models.py
from app import db, login
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
def __repr__(self):
return '<User {}>'.format(self.username)
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
#login.user_loader
def load_user(id):
return User.query.get(int(id))
To create this database, run these commands in your terminal:
# create a migration repository
(venv)$ flask db init
# Create a migration script
(venv)$ flask db migrate -m 'user table'
# Apply the changes
(venv)$ flask db upgrade
This will create a User table with columns for username, email and a hashed password. It is advised not to be storing a user's password in the database, rather store it as a representation of it in the form of a hash.
4. Simple form structure
The register form will look like this:
# register.html
{% extends "base.html" %}
{% block content %}
<h1>Register</h1>
<form action="" method="post">
{{ form.hidden_tag() }}
<p>
{{ form.username.label }}<br>
{{ form.username(size=32) }}<br>
{% for error in form.username.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>
{{ form.email.label }}<br>
{{ form.email(size=32) }}<br>
{% for error in form.email.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>
{{ form.password.label }}<br>
{{ form.password(size=32) }}<br>
{% for error in form.password.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>
{{ form.password2.label }}<br>
{{ form.password2(size=32) }}<br>
{% for error in form.password2.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>{{ form.submit() }}</p>
</form>
{% endblock %}
The login form will look like this:
# login.html
{% 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) }}<br>
{% for error in form.username.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>
{{ form.password.label }}<br>
{{ form.password(size=32) }}<br>
{% for error in form.password.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>{{ form.remember_me() }} {{ form.remember_me.label }}</p>
<p>{{ form.submit() }}</p>
</form>
<p>New here? Register now!</p>
{% endblock %}
Both these templates are inheriting the structure of the base template using the keyword extends. So, this base.html file will look like this:
# base.html
<html>
<head>
{% if title %}
<title>{{ title }} - demo</title>
{% else %}
<title>so demo</title>
{% endif %}
</head>
<body>
<div>
SO Demo:
Home
{% if current_user.is_anonymous %}
Login
{% else %}
Logout
{% endif %}
</div>
<hr>
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</body>
</html>
Every time a user tries to register or log in to their accounts, flash message will appear. This is shown in the base template.
The home page will look like this:
# home.html
{% extends "base.html" %}
{% block content %}
<p>
Hi {{ current_user.username }},<br><br>
These are your details:
</p>
<p>
Name: {{ current_user.username }}<br>
Email: {{ current_user.email }}<br>
Hashed Password: {{ current_user.password_hash }}
</p>
{% endblock %}
We are displaying the current_user's details as stored in the database.
The view functions that handle all these information includes the home(), register(), login() and additionally logout() as seen here:
from flask import render_template, flash, redirect, url_for, request
from app import app, db
from app.forms import LoginForm, RegistrationForm
from flask_login import current_user, login_user, logout_user
from app.models import User
from flask_login import login_required
from werkzeug.urls import url_parse
#app.route('/')
#app.route('/home')
#login_required
def home():
return render_template('home.html')
#app.route('/login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('index'))
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
if user is None or not user.check_password(form.password.data):
flash('Invalid username or password')
return redirect(url_for('login'))
login_user(user, remember=form.remember_me.data)
next_page = request.args.get('next')
if not next_page or url_parse(next_page).netloc != '':
next_page = url_for('home')
return redirect(next_page)
return render_template('login.html', title='Sign In', form=form)
#app.route('/logout')
def logout():
logout_user()
return redirect(url_for('home'))
#app.route('/register', methods=['GET', 'POST'])
def register():
if current_user.is_authenticated:
return redirect(url_for('home'))
form = RegistrationForm()
if form.validate_on_submit():
user = User(username=form.username.data, email=form.email.data)
user.set_password(form.password.data)
db.session.add(user)
db.session.commit()
flash('Congratulations, you are now a registered user!')
return redirect(url_for('login'))
return render_template('register.html', title='Register', form=form)
You should have this:

Why do I keep getting the 405 error (Method Not Allowed The method is not allowed for the requested URL.) when the POST method is correctly set up?

I am learning Flask and I've incurred into a supposedly beginners error:
Method Not Allowed The method is not allowed for the requested URL.
The thing is that, even though I reduce the code into something really simple, I can't find it. As far as I can see, the POST method is correctly set up (I will use comments to explain it).
The files are:
flaskblog.py
from flask import Flask, render_template, url_for, flash, redirect, request
from forms import RegistrationForm
app = Flask(__name__)
app.config['SECRET_KEY'] = '5791628bb0b13ce0c676dfde280ba245'
#app.route("/")
#app.route('/home', methods=['GET', 'POST']) # As one can see, the POST method is written there.
def home():
form = RegistrationForm()
return render_template('home.html', title='Home', form=form)
if __name__ == '__main__':
app.run(debug=True)
home.html
{% extends "layout.html" %}
{% block content %}
<div class="content-section">
<form method="POST" action=""> # The POST method is written there as well.
{{ form.hidden_tag() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Join Today</legend>
<div class="form-group">
{{ form.username.label(class="form-control-label") }}
{{ form.username(class="form-control form-control-lg") }}
</div>
</fieldset>
<div class="form-group">
{{ form.submit(class="btn btn-outline-info") }}
</div>
</form>
</div>
{% endblock content %}
forms.py
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField, BooleanField
from wtforms.validators import DataRequired, Length, Email, EqualTo
class RegistrationForm(FlaskForm):
username = StringField('Username', validators=[DataRequired(), Length(min=2, max=20)])
submit = SubmitField('Sign Up')
I get the error when I submit the form, any help would be greatly appreciated!
Thanks!
You're not distinguishing between GET and POST requests appropriately in your home route. Flask defaults the incoming request, from the client, as a GET request since your definitions are off. However, this is an issue because the incoming request is actually a POST request as we can see from your form:
<form method="POST" action=""> # The POST method is written there as well.
Perhaps you could try modifying your home route to handle both POST/GET requests approriately, something along the lines of:
#app.route("/")
#app.route('/home', methods=['GET', 'POST'])
def home():
form = RegistrationForm()
# POST request
if form.validate_on_submit():
return redirect(url_for(<route>)) #route to be rendered if form properly validates
# GET request
return render_template('home.html', title='Home', form=form)
Hopefully that helps!

No validation on submit Flask

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 get URL not found I use Flask render_template method

I am writing an application which is managing forms using Flask as backend framework.
When I fill data in the form(username and password) correctly, instead of the program returning 'Form sucessfully submitted', I get the following error:
Not Found
The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
Why is that happening?
Notice 1: I put a HTML file in templates directory, so that is not a cause of the error
Notice 2: I wrote here only the code which is relevant to this problem
Here is the code:
1) application.py
from flask import Flask, render_template, redirect, url_for
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField
from wtforms.validators import InputRequired, Email, Length, AnyOf
from flask_bootstrap import Bootstrap
app = Flask(__name__)
Bootstrap(app)
app.config['SECRET_KEY'] = 'somePassword'
class LoginForm(FlaskForm):
username = StringField('username', validators=[InputRequired(), Email(message='I don\'t like your email.')])
password = PasswordField('password', validators=[InputRequired(), Length(min=5, max=10), AnyOf(['secret', 'password'])])
#app.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
return 'Form sucessfully submitted'
return render_template('loginForm1.html', form=form)
if __name__ == '__main__':
app.run(debug=True)
2) loginForm1.html
{% extends "bootstrap/base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}
WTForms
{% endblock %}
{% block content %}
<div class="container">
<form method="POST" action="/">
<dl>
{{ wtf.quick_form(form)}}
<input type="submit" value="Login" class="btn btn-info">
</dl>
</form>
</div>
{% endblock %}
You've defined the route as /login but your form is posting to /.

Categories