My solution to the Codecademy Project “FlaskFM”. This was a culmination of learning from the Introduction to SQL and Databases for Back-end Web Apps chapter. You’ll note I’ve chosen NOT to host this web app directly on my site. The reason is that I’m still learning, and that means things like error handling and input validation. I’ve covered the learning points for the exercise, but I believe the end result may be vulnerable to certain exploits so it’s only suitable for hosting locally. I should also point out that I haven’t written ALL this code; I have made several entries and refactors to get it working according to a project brief.
The exercise was made more difficult due to a bug in the Codecademy version that prevented creation of the tables. I took the project offline, refactored the offending code using VisualStudio, checked the database integrity using DB Browser, and then ran the app using Flask on a local WebServer. You can download the code from GitHub, then I performed the following steps to get it running using the Command Prompt in VisualStudio (on Windows)
- Make sure flask and flask-sqlalchemy are installed in the virtual environment
- Open the Command Prompt in VisualStudio running through following commands:
C:\FlaskFMFolder> python
>>> from app import db
>>> db.create_all()
>>> exit()
C:\FlaskFMFolder> python add_data.py
- Check that the database song_library.db has been created in the app folder
- Open the database using DB Browser and check it has been populated with some test data
- Back on the Command Prompt, set the variable for Flask and run:
C:\FlaskFMFolder> set FLASK_APP = app.py
C:\FlaskFMFolder> flask run
- The app should now be running locally on port 5000 by default. Check by browsing to http://localhost:5000. You should see the following:

Code is as follows:
app.py
from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
#set the SQLALCHEMY_DATABASE_URI key
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///song_library.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SECRET_KEY'] = 'you-will-never-guess'
#create an SQLAlchemy object named `db` and bind it to your app
db = SQLAlchemy(app)
from models import User, Song, Item, Playlist
#a simple initial greeting
@app.route('/')
@app.route('/index')
def greeting():
return render_template('greeting.html')
# app name
@app.errorhandler(404)
def not_found(e):
return render_template("404.html")
#uncomment the code below here when you are done creating database instance db and models
import routes
routes.py
from flask import Flask
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
from app import app, db
from models import User, Song, Playlist, Item
from flask import render_template, request, url_for, redirect, flash
#A form for inputing new songs via Dashboard
class SongForm(FlaskForm):
title = StringField(label = "Song Title:", validators=[DataRequired()])
artist = StringField(label = "Artist:", validators=[DataRequired()])
submit = SubmitField("Add Song")
#A function we made to check if an item to be added is already in the playlist
def exists(item, playlist):
"""Return a boolean
True if playlist contains item. False otherwise.
"""
for i in playlist: #for each item in playlist
if i.song_id == item.song_id: #check if the primary key is equal
return True
return False
#The home page of FlaskFM
#Lists all the users currently in the database
#renders the home.html template providing the list of current users
@app.route('/profiles')
def profiles():
current_users = User.query.all() #change here to a database query
return render_template('users.html', current_users = current_users)
#Displays profile pages for a user with the user_id primary key
#renders the profile.html template for a specific user, song library and
#the user's playlist
@app.route('/profile/<int:user_id>')
def profile(user_id):
user = User.query.filter_by(id = user_id).first_or_404(description = "No such user found.")
songs = Song.query.all()
my_playlist = Playlist.query.get(user.playlist_id) #change here to a database query
return render_template('profile.html', user = user, songs = songs, my_playlist = my_playlist)
#Adds new songs to a user's playlist from the song library
#redirects back to the profile that issued the addition
@app.route('/add_item/<int:user_id>/<int:song_id>/<int:playlist_id>')
def add_item(user_id, song_id, playlist_id):
new_item = Item(song_id = song_id, playlist_id = playlist_id)
user = User.query.filter_by(id = user_id).first_or_404(description = "No such user found.")
my_playlist = Playlist.query.filter_by(id = user.playlist_id).first()
if not exists(new_item, my_playlist.items):
song = Song.query.get(song_id)
#using db session add the new item
db.session.add(new_item)
#increase the counter for the song associated with the new item
song.n += 1
#commit the database changes here
db.session.commit()
return redirect(url_for('profile', user_id = user_id))
#Remove an item from a user's playlist
#Redirects back to the profile that issues the removal
@app.route('/remove_item/<int:user_id>/<int:item_id>')
def remove_item(user_id, item_id):
#from the Item model, fetch the item with primary key item_id to be deleted
delete_item = Item.query.get(item_id)
#using db.session delete the item
db.session.delete(delete_item)
#commit the deletion
db.session.commit()
return redirect(url_for('profile', user_id = user_id))
#Display the Dashboard page with a form for adding songs
#Renders the dashboard template
@app.route('/dashboard', methods=["GET", "POST"])
def dashboard():
form = SongForm()
if request.method == 'POST' and form.validate():
new_song = Song(title = form.title.data, artist = form.artist.data, n = 1)
#create a new song here
#add it to the database
db.session.add(new_song)
#commit to the database
db.session.commit()
else:
flash(form.errors)
unpopular_songs = Song.query.order_by(Song.n) #add the ordering query here
songs = Song.query.all()
return render_template('dashboard.html', songs = songs, unpopular_songs = unpopular_songs, form = form)
models.py
from app import app, db
#the User model: each user has a username, and a playlist_id foreign key referring
#to the user's Playlist
class User(db.Model):
id = db.Column(db.Integer, primary_key = True)
username = db.Column(db.String(50), index = True, unique = True)
playlist_id = db.Column(db.Integer, db.ForeignKey('playlist.id'))
#representation method
def __repr__(self):
return "{}".format(self.username)
#create the Song model here + add a nice representation method
class Song(db.Model):
id = db.Column(db.Integer, primary_key = True)
artist = db.Column(db.String(50), index = True, unique = False)
title = db.Column(db.String(50), index = True, unique = False)
n = db.Column(db.Integer, index = False, unique = False)
def __repr__(self):
return "{} by {}".format(self.title, self.artist)
#create the Item model here + add a nice representation method
class Item(db.Model):
id = db.Column(db.Integer, primary_key = True)
song_id = db.Column(db.Integer, db.ForeignKey('song.id'))
playlist_id = db.Column(db.Integer, db.ForeignKey('playlist.id'))
def __repr__(self):
return "{}".format(Song.query.get(self.song_id))
#create the Playlist model here + add a nice representation method
class Playlist(db.Model):
id = db.Column(db.Integer, primary_key = True)
items = db.relationship('Item', backref = 'playlist', lazy = 'dynamic', cascade = 'all, delete, delete-orphan')
def __repr__(self):
return "Playlist {}".format(self.id)
add_data.py
#script to add test data
from app import db
from models import Song, Playlist, Item, User
p1 = Playlist(id = 3456)
p2 = Playlist(id = 2342)
p3 = Playlist(id = 4576)
p4 = Playlist(id = 8743)
u1 = User(id = 1, username = "mlky_way", playlist_id = p1.id)
u2 = User(id = 2, username = "martian2", playlist_id = p2.id)
u3 = User(id = 3, username = "andromeda_3", playlist_id = p3.id)
u4 = User(id = 4, username = "calypso123", playlist_id = p4.id)
s1 = Song(id = 1, artist = "Franks Sinatra", title = "Fly me to the Moon", n = 0)
s2 = Song(id = 2, artist = "David Bowie", title = "Space Oddity", n = 0)
s3 = Song(id = 3, artist = "Sting", title = "Walking on the Moon", n = 0)
s4 = Song(id = 4, artist = "Nick Cave & The Bad Seeds", title = "Rings of Saturn", n = 0)
s5 = Song(id = 5, artist = "Babylon Zoo", title = "Spaceman", n = 0)
db.session.add(p1)
db.session.add(p2)
db.session.add(p3)
db.session.add(p4)
db.session.add(u1)
db.session.add(u2)
db.session.add(u3)
db.session.add(u4)
db.session.add(s1)
db.session.add(s2)
db.session.add(s3)
db.session.add(s4)
db.session.add(s5)
db.session.commit()
The HTML templates can be found on GitHub. Screen footage:

