(Originally posted July 20, 2013)
Having been working with Django for a while now, I felt like now would be a good time to learn a different python framework — Flask. Flask seems to boil all the things down to a fantastic minimal development experience. It seems perfect for quick apis, or apps. Django is fantastic for more complex apps with its large community and many extensions. Flask may be great for that too, it’s just not something I’ve seen right off the bat.
In order to learn Flask, I’ve decided to solve a mini-problem that I’ve encountered — managing music at a party. From my experience, many of the parties have just one computer hooked up to the sound system, and it’s somewhat of a free for all as to who can play the music and what they can play. And since this computer may or may not have a specific song the person wants to play. As a result, most of the music is played straight from youtube. This leads to having a terrible, or even non existant, queue system. The vision of this project is to have people be able to join a “room” where anyone can search youtube and upload a link to a song they want to hear and have it be put into the queue for playing. This is a rough outline and surely the project is able to change paths, but for the beginning, this seems like a good path.
Like all good python projects, this one starts with firing up a virtualenv.
1
|
virtualenv --distribute youtube-parties |
Then
1
|
source youtube-parties /bin/activate |
to activate the environment. Then you’re going to want to move to another directory and
1
2
3
|
mkdir youtube-parties cd youtube-parties pip install Flask |
The last line there installs the framework we’re going to use to our pip environment. Now we want to start the app. Obviously we need to begin with the standard hello world app.
1
2
3
4
5
6
7
8
9
|
from flask import Flask app = Flask(__name__) @app .route( "/" ) def hello(): return "Hello World!" if __name__ = = "__main__" : app.run() |
You should see a message saying that there is a local server running on localhost:5000 and if you navigate there you should see “Hello World!”. Very nice and very pointless.
Now to templating. Create a directory for the templates and change to that directory.
1
2
|
mkdir templates cd templates/ |
The templating system Flask uses is called Jinja2. Looking at the docs and running through a few examples (the ones here), it seems very similar to Django’s templating system and can easily be picked up by those unfamiliar. Now we want to add the two files to template. The first is layout.html, which will be the base of the whole site, and index.html, which will be the base of the site.
layout.html
1
2
3
4
5
6
7
8
9
10
|
<!doctype html> < html lang = "en" > < head > < title >Youtube for Parties</ title > </ head > < body > {% block content %} {% endblock %} </ body > </ html > |
index.html
1
2
3
4
5
6
7
|
{% extends "layout.html" %} {% block content %} < h1 >Youtube Party!</ h1 > {% endblock %} |
The gist of is simple where the content block in index get put into the content block in layout. Now change app.py to
1
2
3
4
5
6
7
8
9
|
from flask import Flask, render_template app = Flask(__name__) @app .route( '/' ) def index(): return render_template( 'index.html' ) if __name__ = = '__main__' : app.run() |
and run the app again, navigate to localhost:5000 and see the new html.
The next step is to allow uploads of urls to the site. Right now, since we just want to get something going for us, we don’t need to worry about malicious text or anything, and we’re even going to skip the database for the moment. After changes, the new app.py should look like
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
from flask import Flask, render_template, url_for, request, redirect app = Flask(__name__) dumb_db = [] @app .route( '/' , methods = [ 'GET' , 'POST' ]) def index(): if request.method = = 'POST' : dumb_db.append(request.form[ 'youtube-url' ]) return render_template( 'index.html' , queue = dumb_db) @app .route( '/upload-url' , methods = [ 'POST' ]) def upload_url(): dumb_db.append(request.form[ 'youtube-url' ]) return redirect(url_for( 'index' )) if __name__ = = '__main__' : app.debug = True app.run() |
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
{% extends "layout.html" %} {% block content %} < h1 >Youtube Party!</ h1 > {% for q in queue %} < div > {{q}} </ div > {% endfor %} < form method = "post" > < input type = "text" name = "youtube-url" > < button type = "submit" >Upload</ button > </ form > {% endblock %} |
If you restart the app, you should be able to see a form, and if you type things into the form, and submit, you should see a list of the printed above the form. Some notes about the new code.
1) We need some more imports for app.py.
2) dumb_db is just a list where the values from the submit form are appended. If you restart the app, the values go away and you get a new, blank list.
3) The render_tempate call in index has another variable at the end. This passes the value on to the template for use.
4) The app route decorator for index has a method keyword. This allows for a POST method to go through when the default is only GET request. Note that if you didn’t mention GET along with POST, you wouldn’t be able to process a GET request.
5) We added debug = True in the app module at the bottom. This allows for printouts if there are server errors. Very important if you don’t want to use a whole bunch of pdb.
6) In index.html, we loop the queue in a very pythonic way, printing the item each loop
The last thing we want to do for the time is to make the site look halfway decent. In production, you’d want a webserver (nginx or apache) to do this for you. In development though, all you have to do is create a folder called static in the same folder as app.py, and the development server will serve the files automatically at the uri /static/*. Throughout the tutorial, I’m going to be using the css framework Foundation, made by Zurb. The reason I’m doing this is because I’m getting pretty sick of seeing Bootstrap. Bootstrap is fantastic, but I’m looking for a little change up in scenery. Go to http://foundation.zurb.com/ and click the download button. Unzip the file and move the contents to that newly created static/ folder.
To review, we started with nothing, created a virtualenv, added flask, created a first route, added templates, added support for POSTing urls, and marked up the html into something that looks halfway decent.
Next time, we’ll add a database to the url’s have persistance. We’ll also add some error checking to make sure that the links being added are valid.