Tell you how to work with flask to create a website
The GitHub links for this tutorial are Browse
If name equals main explanation
$ pip install flask
$ python3
>>> import flask
>>> exit()
$ mkdir flask_blog
Cd
to flask_blog
then create flaskblog.py
.
Copy this code to flaskblog.py
from flask import Flask
# Flask => Name of the class
app = Flask(__name__)
# __name__ => Special variables in python, just the name of the module, when run that, __name__ = __main__, will see this later. Basically flask knows where to look for your templates and static files...
@app.route("/")
# a route = what we type in the browser to go to different pages. Route decorators.
def hello():
return "Hello World!"
$ export FLASK_APP=flaskblog.py
$ flask run
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Can change http://127.0.0.1:5000/ to http://localhost:5000/
$ export FLASK_DEBUG=1
or add this code into the main *.py file
if __name__ == '__main__':
app.run(debug=True)
Can run by
$ python3 flaskblog.py
This run the script directly with python (if you don't want to keep setting those environment variables again whenerver shut down the terminal).
Add more route
@app.route("/about")
def about():
return "<h1>About Page</h1>"
Create new folder 'templates'
Create inside 'templates' – home.html & about.html
#home.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Home page</title>
</head>
<body>
<h1>Home Page</h1>
</body>
</html>
Code inside flaskblog.py
from flask import Flask, render_template
...
@app.route("/")
@app.route("/home")
def hello():
return render_template('home.html')
Same code to about.html
Fake data for posts
# flaskblog.py
posts = [
{
'author': 'author no.1',
'title': 'title no.1',
'content': 'blog no.1 content',
'date_posted': 'May 01 2019'
},
{
'author': 'author no.2',
'title': 'title no.2',
'content': 'blog no.2 content',
'date_posted': 'May 02 2019'
}
]
# change route 'home'
return render_template('home.html', posts= posts)
Code inside home.html
and about.html
<!-- <head> -->
{% if title%}
<title>Flask Blog | {{ title }}</title>
{% else %}
<title>Flask Blog</title>
{% endif %}
More blog content to home
page
<!-- <body> -->
{% for post in posts %}
<h1>{{ post.title }}</h1>
<p>Posted by {{ post.author }} on {{post.date_posted }}</p>
<p>{{ post.content }}</p>
{% endfor %}
Create layout.html
inside templates
folder
<!-- layout.html -->
<!DOCTYPE html>
<html lang="en">
<head>
{% if title%}
<title>Flask Blog | {{ title }}</title>
{% else %}
<title>Flask Blog</title>
{% endif %}
</head>
<body>
{% block content %} {% endblock %}
</body>
</html>
<!-- home.html -->
{% extends "layout.html" %}
{% block content %}
{% for post in posts %}
<h1>{{ post.title }}</h1>
<p>Posted by {{ post.author }} on {{ post.date_posted }}</p>
<p>{{ post.content }}</p>
{% endfor %}
{% endblock content %}
<!-- should have "name" inside with endblock {% endblock content %} in case we have many block to manage, so name it like the open block tag for easier later -->
<!-- about.html -->
{% extends "layout.html" %}
{% block content %}
<h1>About Page</h1>
{% endblock content %}
Option + Command + U
to check source page
Now, check the powerful of this inheritance method by using Bootstrap for entire pages of the website.
<!-- layout.html / <body> -->
<div class="container">
{% block content %} {% endblock %}
</div>
Reload the page hard refresh by Command + Shift + R
(clear the cache)
Add code from this github link
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='main.css') }}"
Article layout in home
<article class="media content-section">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2" href="#">{{ post.author }}</a>
<small class="text-muted">{{ post.date_posted }}</small>
</div>
<h2><a class="article-title" href="#">{{ post.title }}</a></h2>
<p class="article-content">{{ post.content }}</p>
</div>
</article>
$ pip install flask-wtf
forms.py
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField, BooleanField
from wtforms.validators import DataRequired, Length, Email, EqualTo
class RegisrationForm(FlaskForm):
username = StringField('Username',
validators=[DataRequired(), Length(min=2, max=20)])
email = StringField('Email',
validators=[DataRequired(), Email()])
password = PasswordField('Password',
validators=[DataRequired()])
confirm_password = PasswordField('Confirm Password',
validators=[DataRequired(), EqualTo('password')])
submit = SubmitField('Sign Up')
class LoginForm(FlaskForm):
email = StringField('Email',
validators=[DataRequired(), Email()])
password = PasswordField('Password',
validators=[DataRequired()])
remember = BooleanField('Remember Me')
submit = SubmitField('Login')
Generate random key for SECRET_KEY
$ python3
>>> import secrets
>>> secrets.token_hex(16)
>>> '2833f00b7d64c20651131b5fa9f65287'
flaskblog.py
from forms import RegistrationForm, LoginForm
app.config = ['SECRET_KEY'] = '2833f00b7d64c20651131b5fa9f65287'
register.html
{% extends "layout.html" %}
{% block content %}
<div class='content-section'>
<form method="POST" action="">
{{ 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>
<div class="form-group">
{{ form.email.label(class="form-control-label")}}
{{ form.email(class="form-control form-control-lg")}}
</div>
<div class="form-group">
{{ form.password.label(class="form-control-label")}}
{{ form.password(class="form-control form-control-lg")}}
</div>
<div class="form-group">
{{ form.confirm_password.label(class="form-control-label")}}
{{ form.confirm_password(class="form-control form-control-lg")}}
</div>
</fieldset>
<div class="form-group">
{{ form.submit(class="btn btn-outline-info")}}
</div>
</form>
</div>
<div class="border-top pt-3">
<small class="text-muted">
Already Have An Account? <a class="ml-2" href="{{ url_for('login') }}">Sign In</a>
</small>
</div>
{% endblock content %}
layout.html
<main role="main" class="container">
<div class="row">
<div class="col-md-8">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">
{{ message }}
</div>
{% endfor %}
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</div>
...
Validate the form's fields
register.html
...fieldset
...legend
<div class="form-group">
{{ form.username.label(class="form-control-label")}}
{% if form.username.errors %}
{{ form.username(class="form-control form-control-lg is-invalid")}}
<div class="invalid-feedback">
{% for error in form.username.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.username(class="form-control form-control-lg")}}
{% endif %}
<div class="form-group">
{{ form.email.label(class="form-control-label")}}
{% if form.email.errors %}
{{ form.email(class="form-control form-control-lg is-invalid")}}
<div class="invalid-feedback">
{% for error in form.email.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.email(class="form-control form-control-lg")}}
{% endif %}
</div>
<!-- add the same to password and confirm_password -->
Copy to login.html
(remove username and confirm_password)
Add methods and condition to route login
flaskblog.py
@app.route("/login", methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
if form.email.data == "admin@blog.com" and form.password.data == "password":
flash('You have been logged in!', 'success')
return redirect(url_for('home'))
else:
flash("Login Unsuccessful. Please check email and password", 'danger')
return render_template('login.html', title='login', form=form)
May 03 2019
$ pip install flask-sqlalchemy
flaskblog.py
from flask_sqlalchemy import SQLAlchemy
...
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
db = SQLAlchemy(app)
...
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
image_file = db.Column(db.String(20), nullable=False, default='default.jpg')
password = db.Column(db.String(60), nullable=False)
def __repr__(self):
return f"User('{self.username}', '{self.email}', '{self.image_file}')"
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
...
from datetime import datetime
Author? One-to-many Relationship between Author(Post) & Author(User)
flaskblog.py
class User(db.Model):
...
posts = db.relationship('Post', backref="author", lazy=True)
class Post(db.Model):
...
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
>>> python3
>>> from flaskblog import db
>>> db.create_all()
>>> from flaskblog import User, Post
>>> user_1 = User(username="luke", email="luke@demo.com", password="password1")
>>> db.session.add(user_1)
>>> user_2 = User(username="luca", email="luca@demo.com", password="password2")
>>> db.session.add(user_2)
>>> db.session.commit()
>>> User.query.all()
[User('luke', 'luke@demo.com', 'default.jpg'), User('luca', 'luca@demo.com', 'default.jpg')]
>>> User.query.first()
User('luke', 'luke@demo.com', 'default.jpg')
>>> User.query.filter_by(username="luke").all()
[User('luke', 'luke@demo.com', 'default.jpg')]
>>> User.query.filter_by(username="luke").first()
User('luke', 'luke@demo.com', 'default.jpg')
>>> user = User.query.filter_by(username="luke").first()
>>> user
User('luke', 'luke@demo.com', 'default.jpg')
>>> user.id
1
>>> user = User.query.get(1)
>>> user
User('luke', 'luke@demo.com', 'default.jpg')
>>> user.posts
[]
>>> user.id
1
>>> post_1 = Post(title="Blog 1", content="First post content", user_id=user.id)
>>> post_2 = Post(title="Blog 2", content="Second post content", user_id=user.id)
>>> db.session.add(post_1)
>>> db.session.add(post_2)
>>> db.session.commit()
>>> user.posts
[User('Blog 1', '2019-05-03 17:16:09.993101'), User('Blog 2', '2019-05-03 17:16:09.995732')]
>>> for post in user.posts:
... print(post.title)
Blog 1
Blog 2
>>> post = Post.query.first()
>>> post
User('Blog 1', '2019-05-03 17:16:09.993101')
>>> post.user_id
1
>>> post.author
User('luke', 'luke@demo.com', 'default.jpg')
# Delete all database
>>> db.drop_all()
# Then create the new fresh one
>>> db.create_all()
# check if it ok
>>> User.query.all()
[]
May 05 2019
Create the models.py
, move this code to the models
from flaskblog.py move to models.py
from flaskblog import db
from datetime import datetime
class User(db.Model):
...
class Post(db.Model):
...
Create folder name flaskblog
, inside create __init__.py
Move these files into "package" flaskblog
__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SECRET_KEY'] = '5791628bb0b13ce0c676dfde280ba245'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
db = SQLAlchemy(app)
from flaskblog import routes
Create routes.py
routes.py
from flask import render_template, url_for, flash, redirect
from flaskblog import app
from flaskblog.models import User, Post
from flaskblog.forms import RegistrationForm, LoginForm
...
flaskblog.py
only left
from flaskblog import app
if __name__ == '__main__':
app.run(debug=True)
Rename flaskblog.py
= run.py
May 04 2019
$ pip install flask-bcrypt
>>> from flask_bcrypt import Bcrypt
>>> bcrypt = Bcrypt()
>>> bcrypt.generate_password_hash('testing')
b'$2b$12$e9mzug5NoJCwYUlH/aNp2.Q9zIodxiKMGrLAHO0x.BO4NeHzm2HZW'
>>> bcrypt.generate_password_hash('testing').decode('utf-8')
'$2b$12$5oH0c023RIKm0QroBwKNcu6Ndh/gflgBJt6A0EzUKUpkV2xVnZnMO'
>>> hashed_pw = bcrypt.generate_password_hash('testing').decode('utf-8')
>>> bcrypt.check_password_hash(hashed_pw, 'password')
False
>>> bcrypt.check_password_hash(hashed_pw, 'testing')
True
__init__.py
...
from flask_bcrypt import Bcrypt
...
bcrypt = Bcrypt(app)
from flaskblog import routes
routes.py
...
from flaskblog import app, db, bcrypt
@app.route("/register", methods=['GET', 'POST'])
def register():
form = RegistrationForm()
if form.validate_on_submit():
hashed_password = bcrypt.generate_password_hash(form.password.data).decode('utf-8')
user = User(username=form.username.data, email=form.email.data, password=hashed_password)
db.session.add(user)
db.session.commit()
flash('Your account has been created! You are now able to login', 'success')
return redirect(url_for('login'))
return render_template('register.html', title='Register', form=form)
>>> from flaskblog import db
>>> from flaskblog .models import User
>>> user = User.query.first()
>>> user
User('lululu', 'lululu@gmail.com', 'default.jpg')
>>> user.password
'$2b$12$f/av35Sg02H8UopgGbt0VO6pBke9kXlTNV8rvLInyH.jO3iPOdpmy'
forms.py
def validate_field(self, field):
if True:
raise ValidationError('Validation message')
=====
def validate_username(self, username):
user = User.query.filter_by(username=username.data).first()
if user:
raise ValidationError('That username is taken, please choose another one')
from flaskblog.models import User
After creating account, user can login and log out
$ pip install flask-login
__init__.py
...
from flask_login import LoginManager
...
login_manager = LoginManager(app)
models.py
...
from flask_login import Usermixin
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
...
class User(db.Model, UserMixin):
...
routes.py
from flask_login import login_user
...
def login():
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user and bcrypt.check_password_hash(user.password, form.password.data):
login_user(user, remember=form.remember.data)
return redirect(url_for('home'))
else:
flash('Login Unsuccessful. Please check email and password', 'danger')
return render_template('login.html', title='Login', form=form)
routes.py
from flask_login import login_user, current_user
...
def register():
if current_user.is_authenticated:
return redirect(url_for('home'))
...
def login():
if current_user.is_authenticated:
return redirect(url_for('home'))
...
routes.py
from flask_login import login_user, current_user, logout_user
...
def register():
if current_user.is_authenticated:
return redirect(url_for('home'))
...
def login():
if current_user.is_authenticated:
return redirect(url_for('home'))
...
layout.html
<div class="navbar-nav">
{% if current_user.is_authenticated %}
<a class="nav-item nav-link" href="{{ url_for('logout') }}">Logout</a>
{% else %}
<a class="nav-item nav-link" href="{{ url_for('login') }}">Login</a>
<a class="nav-item nav-link" href="{{ url_for('register') }}">Register</a>
{% endif %}
routes.py
@app.route("/account")
def account():
return render_template('account.html', title='Account')
account.html
{% extends "layout.html" %}
{% block content %}
<h1>{{ current_user.username }}</h1>
{% endblock content %}
layout.html
<div class="navbar-nav">
{% if current_user.is_authenticated %}
<a class="nav-item nav-link" href="{{ url_for('account') }}">Account</a>
<a class="nav-item nav-link" href="{{ url_for('logout') }}">Logout</a>
routes.py
from flask_login import ... login_required
...
@app.route("/account")
@login_required
def account():
...
__init__.py
...
login_manager.login_view ='login'
http://127.0.0.1:5000/account => http://127.0.0.1:5000/login?next=%2Faccount
from flask import ... redirect, request
...
def login():
...
next_page = request.args.get('next')
return redirect(next_page) if next_page else redirect(url_for('home'))
Python
Corey Schafer
Flask
Full Web Application I