diff --git a/README.md b/README.md
index cc852f0..4d17f1a 100644
--- a/README.md
+++ b/README.md
@@ -25,3 +25,9 @@ $ python app.py
```
Visit [http://localhost:5000/](http://localhost:5000/) in your browser to see the results.
+
+### Running Tests
+
+```
+python test_models.py
+```
diff --git a/desserts.db b/desserts.db
new file mode 100644
index 0000000..b960a6d
Binary files /dev/null and b/desserts.db differ
diff --git a/models.py b/models.py
index e40d530..e23fba1 100644
--- a/models.py
+++ b/models.py
@@ -1,4 +1,5 @@
from app import db
+from flask import session
class Dessert(db.Model):
@@ -13,10 +14,14 @@ class Dessert(db.Model):
price = db.Column(db.Float)
calories = db.Column(db.Integer)
- def __init__(self, name, price, calories):
+ user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
+ user = db.relationship("User", backref="desserts")
+
+ def __init__(self, name, price, calories, user_id):
self.name = name
self.price = price
self.calories = calories
+ self.user_id = user_id
def calories_per_dollar(self):
if self.calories:
@@ -32,20 +37,197 @@ def __init__(self, name):
self.name = name
-def create_dessert(new_name, new_price, new_calories):
+class User(db.Model):
+ id = db.Column(db.Integer, primary_key=True)
+ username = db.Column(db.String(100))
+ password = db.Column(db.String(100))
+ email = db.Column(db.String(250))
+ name = db.Column(db.String(100))
+ avatar = db.Column(db.String(250))
+
+ def __init__(self, username, password, email, name, avatar):
+ self.username = username
+ self.password = password
+ self.email = email
+ self.name = name
+ self.avatar = avatar
+
+
+def get_password(username):
+ user = User.query.filter_by(username=username).first()
+ return user.password
+
+
+def list_users():
+ return User.query.all()
+
+
+def get_user(id):
+ return User.query.get(id)
+
+
+def get_user_by_username(username):
+ return User.query.filter_by(username=username).first()
+
+
+def create_user(username, email, password, realname, avatar):
+ user = User(username, email, password, realname, avatar)
+ db.session.add(user)
+ db.session.commit()
+ return user
+
+
+def update_user(id, username=None, email=None, password=None, realname=None, avatar=None):
+
+ user = User.query.get(id)
+
+ if username:
+ user.username = username
+
+ if email:
+ user.email = email
+
+ if password:
+ user.password = password
+
+ if realname:
+ user.realname = realname
+
+ if avatar:
+ user.avatar = avatar
+
+ db.session.commit()
+ return user
+
+
+def login(username, password):
+ # check if user exists in database
+ user = get_user_by_username(username)
+ if user is None:
+ raise Exception("Sorry, we don't have your username on file.")
+
+ # check if password is correct
+ db_password = get_password(username)
+ if password != db_password:
+ raise Exception("Incorrect password.")
+
+ # store username in Flask session
+ session["username"] = username
+
+
+def get_logged_in_user():
+
+ # get stored username from Flask session
+ current_user = session["username"]
+
+ # get user object
+ user = get_user_by_username(current_user)
+
+ # return user object
+ return user
+
+
+def get_desserts_by_user(username):
+
+ # get user object
+ user = get_user_by_username(username)
+
+ # query all desserts matching id of user object
+ desserts = Dessert.query.filter_by(user_id=user.id).all()
+ return desserts
+
+
+def edit_dessert(user, new_name, new_price, new_calories):
+
+ # we need every piece of input to be provided
+ if new_name is None or new_price is None or new_calories is None:
+ raise Exception("Need name, price, and calories!")
+
+ if new_name == '' or new_price == '' or new_calories == '':
+ raise Exception("Need name, price, and calories!")
+
+ # check reasonable number of calories
+ if int(new_calories) < 100:
+ raise Exception("That's not enough calories!")
+ elif int(new_calories) > 3000:
+ raise Exception("That's too many calories!")
+
+ # get dessert from database
+ dessert = Dessert.query.filter_by(name=new_name).first()
+
+ # users can only edit their own desserts
+ if user.id != dessert.user_id:
+ raise Exception("That's not your dessert!")
+
+ dessert.price = new_price
+ dessert.calories = new_calories
+ db.session.commit()
+
+
+def create_dessert(new_name, new_price, new_calories, new_user_id):
# Create a dessert with the provided input.
- # At first, we will trust the user.
- # This line maps to line 16 above (the Dessert.__init__ method)
- dessert = Dessert(new_name, new_price, new_calories)
+ # We need every piece of input to be provided.
+
+ # Can you think of other ways to write this following check?
+ if new_name is None or new_price is None or new_calories is None or new_user_id is None:
+ raise Exception("Need name, price, calories, and user!")
+
+ # They can also be empty strings if submitted from a form
+ if new_name == '' or new_price == '' or new_calories == '' or new_user_id == '':
+ raise Exception("Need name, price, calories, and user!")
+
+ # check for duplicate items
+ name_check = Dessert.query.filter_by(name=new_name).first()
+ if name_check:
+ if new_name == name_check.name:
+ raise Exception("That dessert already exists!")
+ # check reasonable number of calories
+ if int(new_calories) < 100:
+ raise Exception("That's not enough calories!")
+ elif int(new_calories) > 3000:
+ raise Exception("That's too many calories!")
+
+ # This line maps to line 16 above (the Dessert.__init__ method)
+ dessert = Dessert(new_name, new_price, new_calories, new_user_id)
# Actually add this dessert to the database
db.session.add(dessert)
# Save all pending changes to the database
- db.session.commit()
- return dessert
+ try:
+ db.session.commit()
+ return dessert
+ except:
+ # If something went wrong, explicitly roll back the database
+ db.session.rollback()
+
+
+def delete_dessert(user, id):
+
+ # get dessert from database
+ dessert = Dessert.query.get(id)
+
+ # users can only delete desserts they created
+ if user.id != dessert.user_id:
+ raise Exception("That's not your dessert!")
+
+ if dessert:
+ # We store the name before deleting it, because we can't access it
+ # afterwards.
+ dessert_name = dessert.name
+ db.session.delete(dessert)
+
+ try:
+ db.session.commit()
+ return "Dessert {} deleted".format(dessert_name)
+ except:
+ # If something went wrong, explicitly roll back the database
+ db.session.rollback()
+ return "Something went wrong"
+ else:
+ return "Dessert not found"
if __name__ == "__main__":
diff --git a/templates/add.html b/templates/add.html
index e2d174a..7dd1883 100644
--- a/templates/add.html
+++ b/templates/add.html
@@ -1,27 +1,82 @@
-
Add Dessert
+{% include 'header.html' %}
-{% if dessert %}
+
-
{{dessert.name}} successfully created! Add another below.
+
+
\ No newline at end of file
diff --git a/views.py b/views.py
index 4e42ae5..0878751 100644
--- a/views.py
+++ b/views.py
@@ -1,22 +1,95 @@
-from flask import render_template, request
+from flask import render_template, request, session
-from models import Dessert, create_dessert
+from models import Dessert, create_dessert, delete_dessert, edit_dessert, login, get_logged_in_user
from app import app
-@app.route('/')
+@app.route('/', methods=["GET", "POST"])
def index():
- desserts = Dessert.query.all()
+ if request.method == 'GET':
+ # if user is logged in, show only their desserts
+ if "username" in session:
+ user = get_logged_in_user()
+ desserts = Dessert.query.filter_by(user_id=user.id).all()
+ return render_template("menu.html", user=user.name, desserts=desserts)
+ # if no user is logged in, show all desserts
+ else:
+ desserts = Dessert.query.all()
+ return render_template("menu.html", user=None, desserts=desserts)
+
+ # post method is user attempting to log in
+ # get username and password from posted login form
+ username = request.form.get('username_field')
+ password = request.form.get('password_field')
+
+ try:
+ # login() from models.py
+ login(username, password)
+ except Exception as e:
+ return render_template("index.html", error=e.message)
+
+ # once logged in, show only user's desserts
+ user = get_logged_in_user()
+ desserts = Dessert.query.filter_by(user_id=user.id).all()
+ return render_template("menu.html", user=user.name, desserts=desserts)
+
+
+@app.route("/login")
+def login_user():
+ return render_template("index.html")
+
+
+@app.route("/menu")
+def menu():
+ user = get_logged_in_user()
- return render_template('index.html', desserts=desserts)
+ desserts = Dessert.query.filter_by(user_id=user.id).all()
+ return render_template('menu.html', user=user.name, desserts=desserts)
+
+
+@app.route("/logout")
+def logout():
+ session.pop('username',None)
+ return index()
+
+@app.route('/edit/', methods=['GET', 'POST'])
+def edit(id):
+
+ dessert = Dessert.query.get(id)
+
+ if request.method == 'GET':
+ if "username" in session:
+ user = get_logged_in_user()
+ return render_template('edit.html', user=user.name, dessert=dessert)
+ else:
+ return render_template('details.html', user=None, dessert=dessert, error="You must be logged in to edit a dessert.")
+
+ user = get_logged_in_user()
+
+ dessert_name = dessert.name
+ dessert_price = request.form.get('price_field')
+ dessert_cals = request.form.get('cals_field')
+
+ try:
+ dessert = edit_dessert(user, dessert_name, dessert_price, dessert_cals)
+ desserts = Dessert.query.filter_by(user_id=user.id).all()
+ return render_template('menu.html', user=user.name, desserts=desserts)
+ except Exception as e:
+ return render_template('edit.html', user=user.name, dessert=dessert, error=e.message)
+
@app.route('/add', methods=['GET', 'POST'])
def add():
if request.method == 'GET':
- return render_template('add.html')
+ if "username" in session:
+ user = get_logged_in_user()
+ return render_template('add.html', user=user.name)
+ else:
+ desserts = Dessert.query.all()
+ return render_template("menu.html", user=None, desserts=desserts, message="You must be logged in to add a dessert.")
# Because we 'returned' for a 'GET', if we get to this next bit, we must
# have received a POST
@@ -25,9 +98,46 @@ def add():
# The values on the right, inside get(), correspond to the 'name'
# values in the HTML form that was submitted.
+ user = get_logged_in_user()
dessert_name = request.form.get('name_field')
dessert_price = request.form.get('price_field')
dessert_cals = request.form.get('cals_field')
+
+ # Now we are checking the input in create_dessert, we need to handle
+ # the Exception that might happen here.
+
+ # Wrap the thing we're trying to do in a 'try' block:
+ try:
+ dessert = create_dessert(dessert_name, dessert_price, dessert_cals, user.id)
+ return render_template('add.html', user=user.name, dessert=dessert)
+ except Exception as e:
+ # Oh no, something went wrong!
+ # We can access the error message via e.message:
+ return render_template('add.html', error=e.message)
+
+
+@app.route('/desserts/')
+def view_dessert(id):
+
+ dessert = Dessert.query.get(id)
+
+ if "username" in session:
+ user = get_logged_in_user()
+ return render_template('details.html', user=user.name, dessert=dessert)
+ else:
+ return render_template('details.html', user=None, dessert=dessert)
+
+
+@app.route('/delete/')
+def delete(id):
+
+ if "username" in session:
+ user = get_logged_in_user()
+ message = delete_dessert(user, id)
+ return index()
+ else:
+ dessert = Dessert.query.get(id)
+ return render_template('details.html', user=None, dessert=dessert, error="You must be logged in to delete a dessert.")
+
- dessert = create_dessert(dessert_name, dessert_price, dessert_cals)
- return render_template('add.html', dessert=dessert)
+app.secret_key = "desserts_app!"