-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Sebastián Rojas Ortiz
authored and
Sebastián Rojas Ortiz
committed
Oct 28, 2020
0 parents
commit e4900bd
Showing
21 changed files
with
815 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
include flaskr/schema.sql | ||
graft flaskr/static | ||
graft flaskr/templates | ||
global-exclude *.pyc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import os | ||
|
||
from flask import Flask | ||
|
||
|
||
def create_app(test_config=None): | ||
# create and configure the app | ||
app = Flask(__name__, instance_relative_config=True) | ||
app.config.from_mapping( | ||
SECRET_KEY='dev', | ||
DATABASE=os.path.join(app.instance_path, 'flaskr.sqlite'), | ||
) | ||
|
||
if test_config is None: | ||
# load the instance config, if it exists, when not testing | ||
app.config.from_pyfile('config.py', silent=True) | ||
else: | ||
# load the test config if passed in | ||
app.config.from_mapping(test_config) | ||
|
||
# ensure the instance folder exists | ||
try: | ||
os.makedirs(app.instance_path) | ||
except OSError: | ||
pass | ||
|
||
# a simple page that says hello | ||
@app.route('/hello') | ||
def hello(): | ||
return 'Hello, World!' | ||
|
||
from . import db | ||
db.init_app(app) | ||
|
||
from . import auth | ||
app.register_blueprint(auth.bp) | ||
|
||
from . import blog | ||
app.register_blueprint(blog.bp) | ||
app.add_url_rule('/', endpoint='index') | ||
|
||
return app | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import functools | ||
|
||
from flask import ( | ||
Blueprint, flash, g, redirect, render_template, request, session, url_for | ||
) | ||
from werkzeug.security import check_password_hash, generate_password_hash | ||
|
||
from flaskr.db import get_db | ||
|
||
bp = Blueprint('auth', __name__, url_prefix='/auth') | ||
|
||
@bp.route('/register', methods=('GET', 'POST')) | ||
def register(): | ||
if request.method == 'POST': | ||
username = request.form['username'] | ||
password = request.form['password'] | ||
db = get_db() | ||
error = None | ||
|
||
if not username: | ||
error = 'Username is required.' | ||
elif not password: | ||
error = 'Password is required.' | ||
elif db.execute( | ||
'SELECT id FROM user WHERE username = ?', (username,) | ||
).fetchone() is not None: | ||
error = 'User {} is already registered.'.format(username) | ||
|
||
if error is None: | ||
db.execute( | ||
'INSERT INTO user (username, password) VALUES (?, ?)', | ||
(username, generate_password_hash(password)) | ||
) | ||
db.commit() | ||
return redirect(url_for('auth.login')) | ||
|
||
flash(error) | ||
|
||
return render_template('auth/register.html') | ||
|
||
@bp.route('/login', methods=('GET', 'POST')) | ||
def login(): | ||
if request.method == 'POST': | ||
username = request.form['username'] | ||
password = request.form['password'] | ||
db = get_db() | ||
error = None | ||
user = db.execute( | ||
'SELECT * FROM user WHERE username = ?', (username,) | ||
).fetchone() | ||
|
||
if user is None: | ||
error = 'Incorrect username.' | ||
elif not check_password_hash(user['password'], password): | ||
error = 'Incorrect password.' | ||
|
||
if error is None: | ||
session.clear() | ||
session['user_id'] = user['id'] | ||
return redirect(url_for('index')) | ||
|
||
flash(error) | ||
|
||
return render_template('auth/login.html') | ||
|
||
|
||
@bp.before_app_request | ||
def load_logged_in_user(): | ||
user_id = session.get('user_id') | ||
|
||
if user_id is None: | ||
g.user = None | ||
else: | ||
g.user = get_db().execute( | ||
'SELECT * FROM user WHERE id = ?', (user_id,) | ||
).fetchone() | ||
|
||
|
||
@bp.route('/logout') | ||
def logout(): | ||
session.clear() | ||
return redirect(url_for('index')) | ||
|
||
|
||
def login_required(view): | ||
@functools.wraps(view) | ||
def wrapped_view(**kwargs): | ||
if g.user is None: | ||
return redirect(url_for('auth.login')) | ||
|
||
return view(**kwargs) | ||
|
||
return wrapped_view |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
from flask import ( | ||
Blueprint, flash, g, redirect, render_template, request, url_for | ||
) | ||
from werkzeug.exceptions import abort | ||
|
||
from flaskr.auth import login_required | ||
from flaskr.db import get_db | ||
|
||
bp = Blueprint('blog', __name__) | ||
|
||
@bp.route('/') | ||
def index(): | ||
db = get_db() | ||
posts = db.execute( | ||
'SELECT p.id, title, body, created, author_id, username' | ||
' FROM post p JOIN user u ON p.author_id = u.id' | ||
' ORDER BY created DESC' | ||
).fetchall() | ||
return render_template('blog/index.html', posts=posts) | ||
|
||
@bp.route('/create', methods=('GET', 'POST')) | ||
@login_required | ||
def create(): | ||
if request.method == 'POST': | ||
title = request.form['title'] | ||
body = request.form['body'] | ||
error = None | ||
|
||
if not title: | ||
error = 'Title is required.' | ||
|
||
if error is not None: | ||
flash(error) | ||
else: | ||
db = get_db() | ||
db.execute( | ||
'INSERT INTO post (title, body, author_id)' | ||
' VALUES (?, ?, ?)', | ||
(title, body, g.user['id']) | ||
) | ||
db.commit() | ||
return redirect(url_for('blog.index')) | ||
|
||
return render_template('blog/create.html') | ||
|
||
def get_post(id, check_author=True): | ||
post = get_db().execute( | ||
'SELECT p.id, title, body, created, author_id, username' | ||
' FROM post p JOIN user u ON p.author_id = u.id' | ||
' WHERE p.id = ?', | ||
(id,) | ||
).fetchone() | ||
|
||
if post is None: | ||
abort(404, "Post id {0} doesn't exist.".format(id)) | ||
|
||
if check_author and post['author_id'] != g.user['id']: | ||
abort(403) | ||
|
||
return post | ||
|
||
@bp.route('/<int:id>/update', methods=('GET', 'POST')) | ||
@login_required | ||
def update(id): | ||
post = get_post(id) | ||
|
||
if request.method == 'POST': | ||
title = request.form['title'] | ||
body = request.form['body'] | ||
error = None | ||
|
||
if not title: | ||
error = 'Title is required.' | ||
|
||
if error is not None: | ||
flash(error) | ||
else: | ||
db = get_db() | ||
db.execute( | ||
'UPDATE post SET title = ?, body = ?' | ||
' WHERE id = ?', | ||
(title, body, id) | ||
) | ||
db.commit() | ||
return redirect(url_for('blog.index')) | ||
|
||
return render_template('blog/update.html', post=post) | ||
|
||
@bp.route('/<int:id>/delete', methods=('POST',)) | ||
@login_required | ||
def delete(id): | ||
get_post(id) | ||
db = get_db() | ||
db.execute('DELETE FROM post WHERE id = ?', (id,)) | ||
db.commit() | ||
return redirect(url_for('blog.index')) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import sqlite3 | ||
|
||
import click | ||
from flask import current_app, g | ||
from flask.cli import with_appcontext | ||
|
||
|
||
def get_db(): | ||
if 'db' not in g: | ||
g.db = sqlite3.connect( | ||
current_app.config['DATABASE'], | ||
detect_types=sqlite3.PARSE_DECLTYPES | ||
) | ||
g.db.row_factory = sqlite3.Row | ||
|
||
return g.db | ||
|
||
|
||
def close_db(e=None): | ||
db = g.pop('db', None) | ||
|
||
if db is not None: | ||
db.close() | ||
|
||
|
||
def init_db(): | ||
db = get_db() | ||
|
||
with current_app.open_resource('schema.sql') as f: | ||
db.executescript(f.read().decode('utf8')) | ||
|
||
|
||
@click.command('init-db') | ||
@with_appcontext | ||
def init_db_command(): | ||
"""Clear the existing data and create new tables.""" | ||
init_db() | ||
click.echo('Initialized the database.') | ||
|
||
|
||
def init_app(app): | ||
app.teardown_appcontext(close_db) | ||
app.cli.add_command(init_db_command) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
DROP TABLE IF EXISTS user; | ||
DROP TABLE IF EXISTS post; | ||
|
||
CREATE TABLE user ( | ||
id INTEGER PRIMARY KEY AUTOINCREMENT, | ||
username TEXT UNIQUE NOT NULL, | ||
password TEXT NOT NULL | ||
); | ||
|
||
CREATE TABLE post ( | ||
id INTEGER PRIMARY KEY AUTOINCREMENT, | ||
author_id INTEGER NOT NULL, | ||
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
title TEXT NOT NULL, | ||
body TEXT NOT NULL, | ||
FOREIGN KEY (author_id) REFERENCES user (id) | ||
); |
Oops, something went wrong.