Skip to content

Commit

Permalink
working
Browse files Browse the repository at this point in the history
  • Loading branch information
Danny Denenberg authored and Danny Denenberg committed Oct 28, 2021
1 parent 89ceba2 commit 0cf1780
Show file tree
Hide file tree
Showing 14 changed files with 557 additions and 31 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Roar
A tweeting app (clone) made with [Flask](https://flask.palletsprojects.com/en/2.0.x/).

When adding to git, do NOT include `venv` and `__pycache__`. Run `pip freeze > requirements.txt` to get all of your dependencies into a requirements file and DO include that.

The `@click.command("init-db")` decorator makes a function call when you type: `flask init-db`. Do this to initialize the database for the first time (it wipes previous data).

### Running

1. `cd` into the main directory.
2. Create and initialize a virtual enviornment with `python3 -m venv venv` and `. venv/bin/activate`.
3. Install the dependencies with `pip install -r requirements.txt`.
4. If you are running the program for the first time and don't have the database set up, run `flask init-db`.
5. Run the program! `flask run`.
73 changes: 44 additions & 29 deletions app.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,53 @@
from flask import Flask, g
import sqlite3
import os

app = Flask(__name__)
from flask import Flask

def connect_db():
sql = sqlite3.connect('./database.db')
sql.row_factory = sqlite3.Row
return sql

def create_app(test_config=None):
"""Create and configure an instance of the Flask application."""
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
# a default secret that should be overridden by instance config
SECRET_KEY="dev",
# store the database in the instance folder
DATABASE=os.path.join(app.instance_path, "password_manager.sqlite"),
)

def get_db():
#Check if DB is there
if not hasattr(g, 'sqlite3'):
g.sqlite3_db = connect_db()
return g.sqlite3_db
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.update(test_config)

#close the connection to the database automatically
@app.teardown_appcontext
def close_db(error):
#if global object has a sqlite database then close it. If u leave it open noone can access it and gets lost in memory causing leaks.
if hasattr(g, 'sqlite_db'):
g.sqlite3_db.close()
# ensure the instance folder exists
try:
os.makedirs(app.instance_path)
except OSError:
pass

@app.route('/')
def index():
return f'<h1>Hello, World!</h1>'
@app.route("/hello")
def hello():
return "Hello, World!"

@app.route('/users')
def viewusers():
db = get_db()
cursor = db.execute('select id, name, age from people')
results = cursor.fetchall()
return f"<h1>The Id is {results[0]['id']}.<br> The Name is {results[0]['name']}. <br> The age is {results[0]['age']}. </h1>"
# register the database commands
import db

db.init_app(app)

if __name__ == '__main__':
app.run(debug = True)
# apply the blueprints to the app
import auth, blog

app.register_blueprint(auth.bp)
app.register_blueprint(blog.bp)

# make url_for('index') == url_for('blog.index')
# in another app, you might define a separate main index here with
# app.route, while giving the blog blueprint a url_prefix, but for
# the tutorial the blog will be the main index
app.add_url_rule("/", endpoint="index")

return app


create_app()
115 changes: 115 additions & 0 deletions auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
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 werkzeug.security import check_password_hash
from werkzeug.security import generate_password_hash

from db import get_db

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


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 = (
get_db().execute("SELECT * FROM user WHERE id = ?", (user_id,)).fetchone()
)


@bp.route("/register", methods=("GET", "POST"))
def register():
"""Register a new user.
Validates that the username is not already taken. Hashes the
password for security.
"""
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."

if error is None:
try:
db.execute(
"INSERT INTO user (username, password) VALUES (?, ?)",
(username, generate_password_hash(password)),
)
db.commit()
except db.IntegrityError:
# The username was already taken, which caused the
# commit to fail. Show a validation error.
error = f"User {username} is already registered."
else:
# Success, go to the login page.
return redirect(url_for("auth.login"))

flash(error)

return render_template("auth/register.html")


@bp.route("/login", methods=("GET", "POST"))
def login():
"""Log in a registered user by adding the user id to the session."""
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:
# store the user id in a new session and return to the index
session.clear()
session["user_id"] = user["id"]
return redirect(url_for("index"))

flash(error)

return render_template("auth/login.html")


@bp.route("/logout")
def logout():
"""Clear the current session, including the stored user id."""
session.clear()
return redirect(url_for("index"))
122 changes: 122 additions & 0 deletions blog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
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 auth import login_required
from db import get_db

bp = Blueprint("blog", __name__)


@bp.route("/")
def index():
"""Show all the posts, most recent first."""
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)


def get_post(id, check_author=True):
"""Get a post and its author by id.
Checks that the id exists and optionally that the current user is
the author.
:param id: id of post to get
:param check_author: require the current user to be the author
:return: the post with author information
:raise 404: if a post with the given id doesn't exist
:raise 403: if the current user isn't the author
"""
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, f"Post id {id} doesn't exist.")

if check_author and post["author_id"] != g.user["id"]:
abort(403)

return post


@bp.route("/create", methods=("GET", "POST"))
@login_required
def create():
"""Create a new post for the current user."""
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")


@bp.route("/<int:id>/update", methods=("GET", "POST"))
@login_required
def update(id):
"""Update a post if the current user is the author."""
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):
"""Delete a post.
Ensures that the post exists and that the logged in user is the
author of the post.
"""
get_post(id)
db = get_db()
db.execute("DELETE FROM post WHERE id = ?", (id,))
db.commit()
return redirect(url_for("blog.index"))
Empty file removed database.db
Empty file.
Binary file added instance/password_manager.sqlite
Binary file not shown.
14 changes: 12 additions & 2 deletions schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,19 @@
-- Drop any existing data and create empty tables.

DROP TABLE IF EXISTS user;
DROP TABLE IF EXISTS post;

CREATE TABLE user (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
age TEXT NOT NULL
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)
);
Loading

0 comments on commit 0cf1780

Please sign in to comment.