learning from 100 Days of Code: The Complete Python Pro Bootcamp for 2022
Flask Login - Documentation
Werkzeug - Documentation
Learning Point - Flask & Flask login
Point 1. Flask - return / download files
from flask import send_from_directory
@app.route('/download')
def download():
filename = request.args.get('filename')
return send_from_directory('static/files/',
filename, as_attachment=True)
Point 2. Multiple inheritance and mixin classes in Python
notes in project
Learning Point - Werkzeug
Point 1. Hash & Salt the password
from werkzeug.security import generate_password_hash
hash_and_salted_psw = generate_password_hash(
request.form['password'],
method='pbkdf2:sha256',
salt_length=8
)
> method$salt$hash
from werkzeug.security import check_password_hash
password = request.form['password']
check_password_hash(% HASH PASSWORD %, password)
> True / False
Authentication Project
main.py
from flask import Flask, render_template, request, url_for, redirect, flash, send_from_directory
from werkzeug.security import generate_password_hash, check_password_hash
from flask_sqlalchemy import SQLAlchemy
from flask_login import UserMixin, login_user, LoginManager, login_required, current_user, logout_user
app = Flask(__name__)
app.config['SECRET_KEY'] = 'any-secret-key-you-choose'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
# ---------- 01. set the login manager in whole app---------- #
login_manager = LoginManager()
login_manager.init_app(app)
# ---------- 02. set the user class with UserMixin ---------- #
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(100), unique=True, nullable=False)
password = db.Column(db.String(100), nullable=False)
name = db.Column(db.String(1000), nullable=False)
# Line below only required once, when creating DB.
# db.create_all()
# ---------- 03. load user ,AUTO check for current user ---------- #
@login_manager.user_loader
def load_user(user_id):
# if user not valid, return None
return User.query.get(user_id)
# ---------- 04. check current user's status ---------- #
@app.route('/')
def home():
# if not logged in, current user = <User 3>
# instead, current user = <flask_login.mixins.AnonymousUserMixin object at % Space %>
return render_template("index.html", logged_in=current_user.is_authenticated)
@app.route('/register', methods=["GET", "POST"])
def register():
if request.method == "POST":
email = request.form['email']
if User.query.filter_by(email=email).first() is not None:
# ---------- 05. Flag: show a msg at once ---------- #
flash("You've already sign up with the Email. Please log in instead.")
return render_template("register.html")
# ---------- 06. Werkzeug.security: hashing & salting ---------- #
hash_and_salted_psw = generate_password_hash(
request.form['password'],
method='pbkdf2:sha256',
salt_length=8
)
new_user = User(
email=email,
password=hash_and_salted_psw,
name=request.form['name']
)
db.session.add(new_user)
db.session.commit()
# ---------- 07. Log in ---------- #
login_user(new_user)
return render_template("secrets.html", name=request.form['name'])
return render_template("register.html")
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
email = request.form['email']
password = request.form['password']
user = User.query.filter_by(email=email).first()
if user is not None:
# ---------- 08. Werkzeug.security: check password ---------- #
if check_password_hash(user.password, password):
login_user(user, remember=True)
# ---------- 09. Different from redirect & render_template ---------- #
# redirect: switch to other pages (function)
# render_template: show the 'html' with in the same url
return redirect(url_for('secrets'))
else:
flash('Password incorrect. Please try again.')
return render_template("login.html")
else:
flash('User is not exist. Please try again.')
return render_template("login.html")
return render_template("login.html")
# ---------- 10. Login required ---------- #
@app.route('/secrets')
@login_required
def secrets():
return render_template("secrets.html", name=current_user.name,
logged_in=current_user.is_authenticated)
@app.route('/logout')
@login_required
def logout():
# ---------- 11. Log out ---------- #
logout_user()
return redirect(url_for('home'))
@app.route('/download')
@login_required
def download():
filename = request.args.get('filename')
return send_from_directory('static/files/', filename, as_attachment=True)
if __name__ == "__main__":
app.run(debug=True)
index.html
{% extends "base.html" %}
{{ super() }}
{% block content %}
<div class="box">
<h1>Flask Authentication</h1>
<!-- 04. check current user's status -->
{% if not logged_in: %}
<a href="{{ url_for('login') }}" class="btn btn-primary btn-block btn-large">Login</a>
<a href="{{ url_for('register') }}" class="btn btn-secondary btn-block btn-large">Register</a>
{% endif %}
</div>
{% endblock %}
login.html
{% extends "base.html" %}
{% block content %}
<div class="box">
<h1>Login</h1>
<form action="{{ url_for('login') }}" method="post">
<!-- 05. Flag: show a msg at once -->
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<p style="color:red;">{{ message }}</p>
{% endfor %}
{% endif %}
{% endwith %}
<input type="text" name="email" placeholder="Email" required="required"/>
<input type="password" name="password" placeholder="Password" required="required"/>
<button type="submit" class="btn btn-primary btn-block btn-large">Let me in.</button>
</form>
</div>
{% endblock %}
完整語法詳見GitHub