17
loading...
This website collects cookies to deliver better user experience
Flask-login
:pip install flask-login
__init__.py
file in the core directory. Import the Login Manager
class from the installed package and initialise the application with it.from flask import Flask
from config import Config
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager #new line
app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)
migrate = Migrate(app, db)
login = LoginManager(app) #new line
auth
in the core directory and add the following files: __init__.py
, forms.py
, models.py
, and views.py
.templates
folder in the auth directory as well. Create a new folder auth
in it and within it create two files login.html
and register.html
.__init__.py
script. This will be set up the same way as that of the task
blueprint.from flask import Blueprint
auth = Blueprint('auth', __name__, template_folder='templates')
from . import views
from .. import db
from werkzeug.security import generate_password_hash, check_password_hash
from .. import login
from flask_login import UserMixin
from ..models import Todo
class User(UserMixin, db.Model):
__tablename__ = 'user'
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))
todo = db.relationship('Todo', backref='author', lazy='dynamic')
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))
UserMixin
class. db.relationship
which is like a one-to-many
relationship. The first argument Todo
passed here is the many
side of the relationship and the one
is author
. This will create an author field in every todo you create. many
posts linked to just one
user. This will ensure that one user doesn't have access to the to-do list of another user.from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField, ValidationError
from wtforms.validators import DataRequired, Length, Email, EqualTo
from .models import User
class LoginForm(FlaskForm):
email = StringField('Email', validators=[DataRequired(), Length(1, 64),
Email()])
password = PasswordField('Password', validators=[DataRequired()])
remember_me = BooleanField('Remember Me')
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('Username already in use.')
def validate_email(self, email):
user = User.query.filter_by(email=email.data).first()
if user is not None:
raise ValidationError('Email already registered.')
pip install email_validator
User
model for validation purposes. This checks whether the email or username is already in the database and raises an error if a similar email or username is found.from flask import render_template, flash, redirect, url_for, request
from flask_login import login_user, logout_user, login_required, \
current_user
from . import auth
from .forms import RegistrationForm, LoginForm
from .models import User
from .. import db
from werkzeug.urls import url_parse
@auth.route('/register', methods=['GET', 'POST'])
def register():
if current_user.is_authenticated:
return redirect(url_for('task.tasks'))
form = RegistrationForm()
if form.validate_on_submit():
user = User(username=form.username.data.lower(), email=form.email.data.lower())
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('auth.login'))
return render_template('auth/register.html', title='Register', form=form)
@auth.route('/login', methods=['GET', 'POST'])
def login():
nologin = False
if current_user.is_authenticated:
return redirect(url_for('task.tasks'))
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data.lower()).first()
if user is None or not user.check_password(form.password.data):
nologin = True
else:
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('task.tasks')
return redirect(next_page)
return render_template('auth/login.html', title='Sign In', form=form, message=nologin)
@auth.route('/logout')
def logout():
logout_user()
return redirect(url_for('index'))
\register
URL, the register function is executed and the first condition provided checks if the user is already logged in. Then the user gets redirected to the index page of the application if this evaluates to true
. Else, the register page is loaded and the form is rendered. Upon submission, the form is validated and the user data is stored. Next, the user is redirected to the login page.\login
URL, the login function is executed and a similar process is repeated here.logout
URL, the user gets logged out.{% extends "base.html" %}
{% block content %}
<a class="brand-logo" href="{{ url_for('index') }}">
<img class="logo" src="{{ url_for('static', filename='Logo.svg') }}">
<div class="brand-logo-name"><strong> ToDo </strong> </div>
</a>
<!-- Display login error message-->
{% if message %}
<div class="alert alert-warning" role="alert">
<span class="closebtns" onclick="this.parentElement.style.display='none';">×</span>Invalid username or password
</div>
{% endif %}
<div class="login">
<form action="" method="post" novalidate class="p-3 border border-2">
{{ form.hidden_tag() }}
<div class="Login-Header">
<h4 class="mb-5">Login</h4>
</div>
<p>
{{ form.email.label }}<br>
{{ form.email(size=32) }}
{% for error in form.email.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>
{{ form.password.label }}<br>
{{ form.password(size=32) }}
{% for error in form.password.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>{{ form.remember_me() }} {{ form.remember_me.label }}</p>
<div class="loginbtn">
{{ form.submit(class="btn btn-primary mt-3") }}
</div>
</form>
<div class=logged_in>
<span>Dont have an account yet?</span>
<a href="{{ url_for('auth.register') }}"> <i class="fa fa-hand-o-right" aria-hidden="true"></i>Register</a>
</div>
</div>
{% endblock %}
{% extends "base.html" %}
{% block content %}
<a class="brand-logo" href="{{ url_for('index') }}">
<img class="logo" src="{{ url_for('static', filename='Logo.svg') }}">
<div class="brand-logo-name"><strong> ToDo </strong> </div>
</a>
<div class="register">
<form action="" method="post" class="p-3 border border-2">
{{ form.hidden_tag() }}
<div class="Register-Header">
<h4 class="has-text-centered mb-5 is-size-3">Register</h4>
</div>
<p>
{{ form.username.label(class="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(class="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(class="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(class="label") }}<br>
{{ form.password2(size=32) }}<br>
{% for error in form.password2.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<div class="registerbtn">
{{ form.submit(class="btn btn-primary mt-3") }}
</div>
</form>
<div class="registered">
<span>Already registered?</span>
<a href="{{ url_for('auth.login') }}"><i class="fa fa-hand-o-right" aria-hidden="true"></i>Login</a>
</div>
</div>
{% endblock %}
auth
blueprint in the __init__.py
file in the core directory. Add the following lines of code above the task
blueprint.from flask import Flask
from config import Configuration
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
app = Flask(__name__)
app.config.from_object(Configuration)
db = SQLAlchemy(app)
migrate = Migrate(app, db)
login = LoginManager(app)
login.login_view = 'auth.login' #new line
# blueprint for auth routes in our app #new blueprint
from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint)
# blueprint for non-authentication parts of the app
from .task import task as task_blueprint
app.register_blueprint(task_blueprint)
from core import views, models
LoginManager
class and the auth
blueprint is registered with the application.models.py
file in the core directory and create the user_id
field that'll be linked to the User model via a ForeignKey
.user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
__init__.py
file in the core directory. Solution can be found herefrom flask import Flask
from config import Configuration
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
from sqlalchemy import MetaData #new line
app = Flask(__name__)
app.config.from_object(Configuration)
db = SQLAlchemy(app)
#new line
naming_convention = {
"ix": 'ix_%(column_0_label)s',
"uq": "uq_%(table_name)s_%(column_0_name)s",
"ck": "ck_%(table_name)s_%(column_0_name)s",
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
"pk": "pk_%(table_name)s",
}
db = SQLAlchemy(metadata=MetaData(naming_convention=naming_convention))
migrate = Migrate(app, db, render_as_batch=True) #new line
login = LoginManager(app)
login.login_view = 'auth.login'
# blueprint for auth routes in our app
from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint)
# blueprint for non-authentication parts of the app
from .task import task as task_blueprint
app.register_blueprint(task_blueprint)
from core import views, models
flask db stamp head
, flask db migrate
and flask db upgrade
to migrate all the changes to your db. The naming convention error should no longer exist.views.py
file in the task directory.from flask import render_template, flash, redirect, url_for, request
from flask_login import login_required, current_user #new line
from .models import Category
from ..models import Todo
from . import task
from .forms import TaskForm
from .. import db
from datetime import datetime
@task.route('/create-task', methods=['GET', 'POST'])
@login_required #new line
def tasks():
check= None
user = current_user #new line
todo= Todo.query.filter_by(author=user) #new line
date= datetime.now()
now= date.strftime("%Y-%m-%d")
form= TaskForm()
form.category.choices =[(category.id, category.name) for category in Category.query.all()]
if request.method == "POST":
if request.form.get('taskDelete') is not None:
deleteTask = request.form.get('checkedbox')
if deleteTask is not None:
todo = Todo.query.filter_by(id=int(deleteTask)).one()
db.session.delete(todo)
db.session.commit()
return redirect(url_for('task.tasks'))
else:
check = 'Please check-box of task to be deleted'
elif form.validate_on_submit():
selected= form.category.data
category= Category.query.get(selected)
todo = Todo(title=form.title.data, date=form.date.data, time= form.time.data, category= category.name, author=user) #new line
db.session.add(todo)
db.session.commit()
flash('Congratulations, you just added a new note')
return redirect(url_for('task.tasks'))
return render_template('task/tasks.html', title='Create Tasks', form=form, todo=todo, DateNow=now, check=check)
login_required
function and also the current_user
variable. Then assign the login_required
function as a decorator to the task
view function. The current logged in user is obtained via the current_user variable imported from the flask_login
package. Todo List
in the database for todos
created by the particular logged in user and it is also assigned to each todo created by the user.\create-task
and you'll get redirected to the login page. create-task
page. If you try to navigate to the /login
or /register
page while still logged in, you still get redirected to the create-task
page.