1

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> 

1 Answers1

4

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:

  1. Create a simple application to ensure that every part of flask is working
  2. Configure your application to work with web forms and a database
  3. Create a simple database (I will use sqlite. You can use any other if you want)
  4. Create registration and login form structures
  5. Display the forms in HTML templates
  6. Render those templates using view functions

Consider using the structure below when building your application. It is convenient 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-dotenv 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 subsequent 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 directory
(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 strongly advised not to store a user's password in the database, rather store it as a representation of itself in the form of a hash.

4. Simple form structure

The registration 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? <a href="{{ url_for('register') }}">Register now!</a></p>
{% endblock %}

Both these templates inherit 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:
            <a href="{{ url_for('home') }}">Home</a>
            {% if current_user.is_anonymous %}
            <a href="{{ url_for('login') }}">Login</a>
            {% else %}
            <a href="{{ url_for('logout') }}">Logout</a>
            {% 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, a 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 display the current_user's details as stored in the database.

The view functions that handle all this 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:

enter image description here

Gitau Harrison
  • 3,179
  • 1
  • 19
  • 23
  • Holy cow, what an amazing answer! I do have one question: in his sample, he was using flask_wtf. Is there a reason you didn't use WTForms? Just wondering. – J. Gwinner Oct 18 '21 at 05:17