An App Framework without too much Framework
One of the pieces that we built was a Web Application framework on top of Werkzeug. I fell in love with Werkzeug as soon as I learned how to spell it. It reminded me so much of how I learned how to do things with web apps in Django (but without the Django) and also has the world's most amazing debugger. I had heard a lot about Flask and how it was werkzeug based, but never explored it, so thought this time around it would be worth using.
I started with the Flask Quickstart tutorial but quickly made a couple changes to how I structured my application as it would scale.
While the URL routing decorator is a nice and easy to use shortcut, I had visions of url decorators distributed in views all over the place in files and folders that will get lost over time in the code base (can you tell I came from a messy codebase?) and felt like organizing them into a single file was important. So I created a urls.py file that would contain all the routing (similar to Django in that it's all in one place.)
from kickbacker import app from kickbacker import views @app.route('/backer/<backer_id>') def respond_backers(backer_id): return views.show_backer(backer_id) @app.route('/project/<project_id>/backers/') def respond_show_backers(project_id): return views.show_backers(project_id) @app.route('/projects') def respond_projects(): return views.show_projects()
I decided that I was going to use redis as my primary database and wanted a way to add that as a global variable to my flask app. Sub-classing the Flask class seemed the best way. While I was in there I decided to define the logging to use a more descriptive format and to use a RotatingFileHandler since I tend to add logging in way more places than I need to.
class KickFlask(Flask): def connect_redis(self): self.rs = redis.Redis(self.config['REDIS_HOST']) self.logger.info("Connected to Redis at %s" % \ (self.config['REDIS_HOST']) ) def setup_logging(self): log_filename = self.config['LOGFILE'] file_handler = RotatingFileHandler(log_filename) if self.config['DEBUG']: file_handler.setLevel(logging.INFO) else: file_handler.setLevel(logging.ERROR) kb_fmt = u'[%(asctime)s %(levelname)s] - %(processName)s (%(process)s) - (%(module)s:%(funcName)s:%(lineno)s) %(message)s' kb_formatter = logging.Formatter(fmt=kb_fmt) file_handler.setFormatter(kb_formatter) self.logger.addHandler(file_handler)
Here is what my __init__.py file looks like, which connects to redis and sets up logging on start.
from kickflask import KickFlask app = KickFlask(__name__) app.config.from_object('kickbacker.settings.DevConfig') app.setup_logging() app.connect_redis() import urls
I also decided to use the handy from_object method to load in different settings classes based on whether the application is running in a Production or a Development environment. For now I only have one environment setup but once a production is setup I can add some if/else logic to look for some file, uname, internal IP or something that is specific to production.
Here is my settings.py file
# Here lie the settings class Config(object): DEBUG=False HOST='0.0.0.0' REDIS_HOST='localhost' LOGFILE='/var/log/kickbacker.log' class ProdConfig(Config): DEBUG=False # Run Server in Debug Mode class DevConfig(Config): DEBUG=True
These changes are going to make it flexible enough for me to organize my urls all in one place, customize my Flask application to any number of random things, and also modify the settings for multiple environments I will end up needing later on (like a testing one.)
I am currently working on a side project where the primary goal is to explore many of the widely-used open source alternatives to some of the pieces we attempted to build in-house at my last job: a werkzeug-powered app framework, a job queue, a local cache layer. But also to explore some of the other technologies that have become popular over the past few years: NoSQL, Node.js and who knows what else.
Posts (so far) in this Series
- Building a Python Web App with Flask
- Installation Automation with Fabric
- Server Deployment with boto
- Private Git Repos Hosted on DropBox