def create(): """Displays the view for creating a new snippet. Requires a logged in user. User will enter a title and body for the snippet. Title is reqired. The values are then stored in the database and associated with the logged in user. """ if request.method == "POST": title = request.form.get("title", None) body = request.form.get("body", None) error = None if not title: error = "Title is required." if error is not None: flash(error) else: db = get_db() db.run_query( "INSERT INTO snippet (title, body, author_id)" + " VALUES (%s, %s, %s)", (title, body, g.user["id"])) return redirect(url_for("snippets.index")) return render_template("snippets/create.html")
def test__get_db(self): fresh_db = None with captured_output(): with self.app_context: fresh_db = psql_db.get_db() fresh_db.load_schema() self.assertIsNotNone(self.db)
def edit(id): """ Displays the entirety of a snippet for the end user to edit. Requires a logged in user Args: id (int): database id number for the snippet """ snippet = get_snippet(id) if request.method == "POST": title = request.form.get("title", None) body = request.form.get("body", None) error = None if not title: error = "Title is required." if error is not None: flash(error) else: db = get_db() db.run_query( "UPDATE snippet SET title = %s, body = %s" + " WHERE id = %s", (title, body, id)) return redirect(url_for("snippets.index")) return render_template("snippets/edit.html", snippet=snippet)
def test__close_db(self): # the close_db method interacts with Flask's g object, which lives # within the request_context rather than the app_context with captured_output(): with self.app.test_request_context() as trc: trc.g.db = psql_db.get_db() trc.g.db.open_connection() psql_db.close_db() self.assertNotEqual(self.db.conn.closed, 0)
def index(): """Queries database for all snippets, which are passed to index.html and displayed""" db = get_db() snippets = db.run_query( "SELECT s.id, title, body, created, author_id, username" " FROM snippet s JOIN users u ON s.author_id = u.id" " ORDER BY created DESC") if not snippets: snippets = [] return render_template("snippets/index.html", snippets=snippets)
def delete(id): """ Deletes a snippet from the database. Requires a logged in user Args: id (int): database id number for the snippet """ # First query db to make sure snippet exists. get_snippet aborts unless a snippet is found get_snippet(id) db = get_db() db.run_query("DELETE FROM snippet WHERE id = %s", (id, )) return redirect(url_for("snippets.index"))
def setUp(self): self.app = create_app(testing="instance.config.TestConfig") # create a test client self.client = self.app.test_client() self.app.testing = True self.app_context = self.app.app_context() # psql_db.init_app(self.app) with captured_output(): with self.app_context: self.db = psql_db.get_db() self.db.load_schema()
def login(): """ Checks user credentials from the login form. Takes the username and password from the login form, and queries against the DB. If a valid user is found, verifies that the password hash matches the expected value. If the inputs are valid, query the database for the associated user_id and store the information in g.user. Also set the session's user_id to the id of the user that just logged in. If user_id in the session is `None`, g.user is set to None """ if request.method == "POST": username = request.form.get("username", None) password = request.form.get("password", None) db = get_db() error = None # Query the database for a user and return all columns # User is unique (primary key), so a single row is returned user = db.run_query("SELECT * FROM users WHERE username = %s", (username, )) if not user: error = "Incorrect username." elif len(user) > 1: # Should never happen if users are added via register, but catch anyways error = "Duplicate users -- login failed" else: user = user[0] g.user = user # check_password_hash is a flask built-in if not check_password_hash(user["password"], password): error = "Incorrect password." if error is None: # session is a dictionary that stores data across requests # When validation succeeds, the user's `id` is stored in # a new session. session.clear() session["user_id"] = user["id"] return redirect(url_for("index")) # Now that the user's `id` is stored in the session, # it will be available on subsequent requests. # At the beginning of each request, if a user is logged in # their information should be loaded and available to views flash(error) return render_template("users/login.html")
def view(id): """ Displays the entirety of a snippet for the end user to view. Also has a POST request option, where the user can click an 'edit' button to edit the snippet. Args: id (int): database id number for the snippet """ snippet = get_snippet(id, check_author=False) if request.method == "POST": db = get_db() db.run_query( "UPDATE snippet SET title = %s, body = %s" + " WHERE id = %s", (snippet["title"], snippet["body"], id)) return redirect(url_for("snippets.index")) return render_template("snippets/view.html", snippet=snippet)
def register(): """ Checks user credentials from the registration form. Takes the username and password from the form, and queries against the DB. If a user is found, returns an error message. Otherwise, adds a new user to the database and stores their password hash. Redirects to the login form if successful. Otherwise redirects to the registration page. """ if request.method == "POST": # `request.form` is a special dictionary that maps form # keys and their values username = request.form.get("username", None) password = request.form.get("password", None) db = get_db() error = None if not username: error = "Username is required." elif not password: error = "Password is required." elif db.run_query("SELECT id FROM users WHERE username = %s", (username, )) is not None: error = f"User {username} is already registered." if error is None: db.run_query( "INSERT INTO users (username, password) VALUES (%s, %s)", # Password is hashed for security (username, generate_password_hash(password))) # `redirect` generates a redirect response to the URL return redirect(url_for("users.login")) # `flash()` stores messages that can be retrieved when # rendering the template (HTML) flash(error) # `render_template()` renders a template containing the HTML return render_template("users/register.html")
def get_snippet(id, check_author=True): """Queries the database for a specific snippet, based on the snippet id. This function is designed as a helper function for the 'view', 'edit', and 'delete' views defined below. Each of the views will reference a specific snippet and the associated snippet id will be fed to this function. Throws errors if the snippet doesn't exist or if there are duplicate snippets. Also has an argument that governs whether the author id needs to match the id of the logged in user. Args: id (int): database id number for the snippet check_author (bool): optional input that govens whether the author id needs to match the id of the logged in user before return a snippet Returns: snippet (RealDictCursor): a snippet from the database """ snippet = get_db().run_query( "SELECT s.id, title, body, created, author_id, username" + " FROM snippet s JOIN users u ON s.author_id = u.id" + " WHERE s.id = %s", (id, )) if snippet is None: # abort() will raise a special exception that returns a # HTTP status code. 404 == 'Not Found' abort(404, f"Snippet id {id} doesn't exist.") elif len(snippet) > 1: # should never happen, but... abort(404, f"Duplicate snippets with id {id}.") else: snippet = snippet[0] if check_author and snippet["author_id"] != g.user["id"]: abort(403) return snippet
def load_logged_in_user(): """ Checks user credentials from the session. If the inputs are valid, query the database for the associated user_id and store the information in g.user. If user_id in the session is `None`, g.user is set to None """ user_id = session.get("user_id") if user_id is None: g.user = None else: print(f"User ID: {user_id}") db = get_db() user = db.run_query("SELECT * FROM users WHERE id = %s", (user_id, )) print(user) if user is None: g.user = None elif len(user) == 1: user = user[0] g.user = user