Skip to content

Commit

Permalink
Remove dependency on Flask-Login in Flaskr example
Browse files Browse the repository at this point in the history
  • Loading branch information
miguelgrinberg committed Oct 28, 2023
1 parent 964e078 commit dd69bc6
Show file tree
Hide file tree
Showing 10 changed files with 45 additions and 32 deletions.
6 changes: 0 additions & 6 deletions examples/flaskr/flaskr/__init__.py
Expand Up @@ -4,11 +4,8 @@
from flask import Flask
from flask.cli import with_appcontext
from alchemical.flask import Alchemical
from flask_login import LoginManager

db = Alchemical()
login = LoginManager()
login.login_view = 'auth.login'


def create_app(test_config=None):
Expand Down Expand Up @@ -41,9 +38,6 @@ def create_app(test_config=None):
db.init_app(app)
app.cli.add_command(init_db_command)

# initialize Flask-Login
login.init_app(app)

# apply the blueprints to the app
from flaskr import auth, blog

Expand Down
41 changes: 32 additions & 9 deletions examples/flaskr/flaskr/auth.py
@@ -1,22 +1,44 @@
import functools

from flask import Blueprint
from flask import flash
from flask import g
from flask import redirect
from flask import render_template
from flask import request
from flask import session
from flask import url_for
from flask_login import login_user
from flask_login import logout_user
from sqlalchemy.exc import IntegrityError

from flaskr import db, login
from flaskr import db
from flaskr.models import User

bp = Blueprint("auth", __name__, url_prefix="/auth")


@login.user_loader
def load_user(id):
return db.session.get(User, int(id))
def login_required(view):
"""View decorator that redirects anonymous users to the login page."""

@functools.wraps(view)
def wrapped_view(**kwargs):
if g.user is None:
return redirect(url_for("auth.login"))

return view(**kwargs)

return wrapped_view


@bp.before_app_request
def load_logged_in_user():
"""If a user id is stored in the session, load the user object from
the database into ``g.user``."""
user_id = session.get("user_id")

if user_id is None:
g.user = None
else:
g.user = db.session.get(User, int(user_id))


@bp.route("/register", methods=("GET", "POST"))
Expand Down Expand Up @@ -60,7 +82,7 @@ def login():
password = request.form["password"]
error = None

query = User.select().filter_by(username=username)
query = User.select().where(User.username == username)
user = db.session.scalar(query)

if user is None:
Expand All @@ -70,7 +92,8 @@ def login():

if error is None:
# store the user id in a new session and return to the index
login_user(user)
session.clear()
session["user_id"] = user.id
return redirect(url_for("index"))

flash(error)
Expand All @@ -81,5 +104,5 @@ def login():
@bp.route("/logout")
def logout():
"""Clear the current session, including the stored user id."""
logout_user()
session.clear()
return redirect(url_for("index"))
8 changes: 4 additions & 4 deletions examples/flaskr/flaskr/blog.py
@@ -1,14 +1,14 @@
from flask import Blueprint
from flask import flash
from flask import g
from flask import redirect
from flask import render_template
from flask import request
from flask import url_for
from werkzeug.exceptions import abort
from flask_login import current_user
from flask_login import login_required

from flaskr import db
from flaskr.auth import login_required
from flaskr.models import Post

bp = Blueprint("blog", __name__)
Expand Down Expand Up @@ -36,7 +36,7 @@ def get_post(id, check_author=True):
if post is None:
abort(404, f"Post id {id} doesn't exist.")

if check_author and post.author != current_user:
if check_author and post.author != g.user:
abort(403)

return post
Expand All @@ -57,7 +57,7 @@ def create():
if error is not None:
flash(error)
else:
db.session.add(Post(title=title, body=body, author=current_user))
db.session.add(Post(title=title, body=body, author=g.user))
db.session.commit()
return redirect(url_for("blog.index"))

Expand Down
8 changes: 3 additions & 5 deletions examples/flaskr/flaskr/models.py
@@ -1,14 +1,12 @@
from werkzeug.security import check_password_hash
from werkzeug.security import generate_password_hash
from flask import url_for
from flask_login import UserMixin
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, func
from sqlalchemy.orm import relationship
from alchemical import Model

from flaskr import db


class User(UserMixin, db.Model):
class User(Model):
id = Column(Integer, primary_key=True)
username = Column(String, unique=True, nullable=False)
password_hash = Column(String, nullable=False)
Expand All @@ -26,7 +24,7 @@ def check_password(self, value):
return check_password_hash(self.password_hash, value)


class Post(db.Model):
class Post(Model):
id = Column(Integer, primary_key=True)
author_id = Column(ForeignKey(User.id), nullable=False)
created = Column(
Expand Down
4 changes: 2 additions & 2 deletions examples/flaskr/flaskr/templates/base.html
Expand Up @@ -4,8 +4,8 @@
<nav>
<h1><a href="{{ url_for('index') }}">Flaskr</a></h1>
<ul>
{% if current_user.is_authenticated %}
<li><span>{{ current_user.username }}</span>
{% if g.user %}
<li><span>{{ g.user.username }}</span>
<li><a href="{{ url_for('auth.logout') }}">Log Out</a>
{% else %}
<li><a href="{{ url_for('auth.register') }}">Register</a>
Expand Down
4 changes: 2 additions & 2 deletions examples/flaskr/flaskr/templates/blog/index.html
Expand Up @@ -2,7 +2,7 @@

{% block header %}
<h1>{% block title %}Posts{% endblock %}</h1>
{% if current_user.is_authenticated %}
{% if g,user %}
<a class="action" href="{{ url_for('blog.create') }}">New</a>
{% endif %}
{% endblock %}
Expand All @@ -15,7 +15,7 @@ <h1>{% block title %}Posts{% endblock %}</h1>
<h1>{{ post.title }}</h1>
<div class="about">by {{ post.author.username }} on {{ post.created.strftime('%Y-%m-%d') }}</div>
</div>
{% if current_user == post.author %}
{% if g.user == post.author %}
<a class="action" href="{{ post.update_url }}">Edit</a>
{% endif %}
</header>
Expand Down
1 change: 0 additions & 1 deletion examples/flaskr/requirements.txt
@@ -1,7 +1,6 @@
alchemical
click==8.0.1
Flask==2.0.1
Flask-Login==0.5.0
greenlet==1.1.1
importlib-metadata==4.6.3
itsdangerous==2.0.1
Expand Down
2 changes: 1 addition & 1 deletion examples/flaskr/tests/test_auth.py
Expand Up @@ -15,7 +15,7 @@ def test_register(client, app):

# test that the user was inserted into the database
with app.app_context():
query = User.select().filter_by(username="a")
query = User.select().where(User.username == "a")
assert db.session.scalar(query) is not None


Expand Down
2 changes: 1 addition & 1 deletion examples/flaskr/tests/test_blog.py
Expand Up @@ -24,7 +24,7 @@ def test_index(client, auth):
@pytest.mark.parametrize("path", ("/create", "/1/update", "/1/delete"))
def test_login_required(client, path):
response = client.post(path)
assert "/auth/login?next=" in response.headers["Location"]
assert "/auth/login" in response.headers["Location"]


def test_author_required(app, client, auth):
Expand Down
1 change: 0 additions & 1 deletion tox.ini
Expand Up @@ -30,7 +30,6 @@ deps=
aiosqlite
alembic
flask
flask-login
pytest
pytest-cov
pytest-asyncio
Expand Down

0 comments on commit dd69bc6

Please sign in to comment.