While building API Jobs I used a lot of things I’ve never used before so I thought I’d do some mini reviews of each of the pieces that went into building the site. I started this as one post but it’s getting really long so I’m going to chop it up.
Part 1: Python and Flask
I had built a couple of toy projects using Python to learn my way around a little bit but this was the first real web site I built with it. Django looked like overkill so I went with Flask because it’s much simpler. It’s essentially a Sinatra for Python.
Python is a great language. It’s terse but expressive. The ecosystem is robust and there’s a package out there for just about everything important. pip is a capable package manager, though managing environments with virtualenv is sort of a pain because of manual activation and deactivation. autoenv may help, but it’s still pretty new.
The simple cases in Flask work almost too easily. Define a method, add a route decorator, save the file and run python web.py
from the command line and you’re up and running. For the most part this site is contained entirely within one web.py, though it’s starting to feel unwieldy. I think I’ll be looking into Blueprints soon. Locally I just use the built-in web server, but when deployed to Heroku it runs on gunicorn at the recommendation of a former coworker who knows things.
I used the default templating language that comes with Flask called Jinja2. It’s a capable templating language. One of my favorite parts is its support for custom filters which are sort of like C# extension methods you can apply to any output block e.g. {{ job.description|striptags }}
. You define them as simple functions and then register them with the Jinja engine.
Some of the pages require sensitive information being transferred so I wanted to easily mark a method as requiring SSL. I was surprised to see this wasn’t built in so I wrote a custom decorator. It worked great locally but when deployed to Heroku it resulted in an endless redirect. Heroku runs your app behind a proxy and if SSL is configured, that’s handled at the proxy level and not the app level so if you inspect the requested URL it’s never using HTTPS. Heroku passes a x-forwarded-proto
header though that includes the original URL. My custom @https_required
code is here.
I played with a few different config setups before settling on a file named config.py that contains some variable assignments. Then I can get access to them like this:
import config
config.TWITTER_CONSUMER_KEY
The config.py does a check for an environment variable called ‘REALM’ and if it matches ‘prod’ resets the variable values to the production versions. Like so. I don’t know if this is the best way to do it, but it works and it’s simple.
The only web logic that lives outside of web.py is the handling for the AJAX actions of the admin interface. I first defined a class that maps the HTTP method and the resource to a method call to handle that action. The actions return a tuple of a dictionary and a status code. Then in the web.py file, I wire up a route to pass through the requests to the command processor and turn the response into JSON. I’m guessing there’s probably an easier way to do all this, but I’m still learning.
One other janky thing I had to do was how to handle redirects from the non-www domain to the www version (Heroku recommends you don’t use the apex domain as the primary one). I wanted to keep the requested paths in tact when redirecting. I couldn’t find the place to hook into Flask to intercept it before it hit any methods. I think signals is what I wanted but my quick attempts to get one to work didn’t pan out. Instead I created a second Heroku app with a catchall route that does the redirection.
Coming up in future posts, my thoughts on Heroku, SendGrid, Searchify, Stripe, Twitter Bootstrap, Redis and more.
Any questions? I’d love to know what you’d like to hear more about.