Chapter 5: User Logins (v0.5)

This commit is contained in:
Miguel Grinberg 2017-09-12 23:31:39 -07:00
parent 68368ea5c5
commit e7b1227a1c
No known key found for this signature in database
GPG Key ID: 36848B262DF5F06C
8 changed files with 122 additions and 12 deletions

View File

@ -1,11 +1,14 @@
from flask import Flask from flask import Flask
from flask_sqlalchemy import SQLAlchemy from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate from flask_migrate import Migrate
from flask_login import LoginManager
from config import Config from config import Config
app = Flask(__name__) app = Flask(__name__)
app.config.from_object(Config) app.config.from_object(Config)
db = SQLAlchemy(app) db = SQLAlchemy(app)
migrate = Migrate(app, db) migrate = Migrate(app, db)
login = LoginManager(app)
login.login_view = 'login'
from app import routes, models from app import routes, models

View File

@ -1,6 +1,7 @@
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired from wtforms.validators import ValidationError, DataRequired, Email, EqualTo
from app.models import User
class LoginForm(FlaskForm): class LoginForm(FlaskForm):
@ -8,3 +9,22 @@ class LoginForm(FlaskForm):
password = PasswordField('Password', validators=[DataRequired()]) password = PasswordField('Password', validators=[DataRequired()])
remember_me = BooleanField('Remember Me') remember_me = BooleanField('Remember Me')
submit = SubmitField('Sign In') submit = SubmitField('Sign In')
class RegistrationForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
email = StringField('Email', validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired()])
password2 = PasswordField(
'Repeat Password', validators=[DataRequired(), EqualTo('password')])
submit = SubmitField('Register')
def validate_username(self, username):
user = User.query.filter_by(username=username.data).first()
if user is not None:
raise ValidationError('Please use a different username.')
def validate_email(self, email):
user = User.query.filter_by(email=email.data).first()
if user is not None:
raise ValidationError('Please use a different email address.')

View File

@ -1,8 +1,10 @@
from datetime import datetime from datetime import datetime
from app import db from app import db, login
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
class User(db.Model): class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True) username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True) email = db.Column(db.String(120), index=True, unique=True)
@ -12,6 +14,17 @@ class User(db.Model):
def __repr__(self): def __repr__(self):
return '<User {}>'.format(self.username) 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))
class Post(db.Model): class Post(db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)

View File

@ -1,12 +1,15 @@
from flask import render_template, flash, redirect, url_for from flask import render_template, flash, redirect, url_for, request
from app import app from flask_login import login_user, logout_user, current_user, login_required
from app.forms import LoginForm from werkzeug.urls import url_parse
from app import app, db
from app.forms import LoginForm, RegistrationForm
from app.models import User
@app.route('/') @app.route('/')
@app.route('/index') @app.route('/index')
@login_required
def index(): def index():
user = {'username': 'Miguel'}
posts = [ posts = [
{ {
'author': {'username': 'John'}, 'author': {'username': 'John'},
@ -17,14 +20,43 @@ def index():
'body': 'The Avengers movie was so cool!' 'body': 'The Avengers movie was so cool!'
} }
] ]
return render_template('index.html', title='Home', user=user, posts=posts) return render_template('index.html', title='Home', posts=posts)
@app.route('/login', methods=['GET', 'POST']) @app.route('/login', methods=['GET', 'POST'])
def login(): def login():
if current_user.is_authenticated:
return redirect(url_for('index'))
form = LoginForm() form = LoginForm()
if form.validate_on_submit(): if form.validate_on_submit():
flash('Login requested for user {}, remember_me={}'.format( user = User.query.filter_by(username=form.username.data).first()
form.username.data, form.remember_me.data)) 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('index')
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('index'))
@app.route('/register', methods=['GET', 'POST'])
def register():
if current_user.is_authenticated:
return redirect(url_for('index')) return redirect(url_for('index'))
return render_template('login.html', title='Sign In', form=form) 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)

View File

@ -11,7 +11,11 @@
<div> <div>
Microblog: Microblog:
<a href="{{ url_for('index') }}">Home</a> <a href="{{ url_for('index') }}">Home</a>
{% if current_user.is_anonymous %}
<a href="{{ url_for('login') }}">Login</a> <a href="{{ url_for('login') }}">Login</a>
{% else %}
<a href="{{ url_for('logout') }}">Logout</a>
{% endif %}
</div> </div>
<hr> <hr>
{% with messages = get_flashed_messages() %} {% with messages = get_flashed_messages() %}

View File

@ -1,7 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
<h1>Hi, {{ user.username }}!</h1> <h1>Hi, {{ current_user.username }}!</h1>
{% for post in posts %} {% for post in posts %}
<div><p>{{ post.author.username }} says: <b>{{ post.body }}</b></p></div> <div><p>{{ post.author.username }} says: <b>{{ post.body }}</b></p></div>
{% endfor %} {% endfor %}

View File

@ -21,4 +21,5 @@
<p>{{ form.remember_me() }} {{ form.remember_me.label }}</p> <p>{{ form.remember_me() }} {{ form.remember_me.label }}</p>
<p>{{ form.submit() }}</p> <p>{{ form.submit() }}</p>
</form> </form>
<p>New User? <a href="{{ url_for('register') }}">Click to Register!</a></p>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,37 @@
{% 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=64) }}<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 %}