Skip to content

Instantly share code, notes, and snippets.

@Flushot
Created March 11, 2015 21:32
Show Gist options
  • Save Flushot/840e549f055dce626dfb to your computer and use it in GitHub Desktop.
Save Flushot/840e549f055dce626dfb to your computer and use it in GitHub Desktop.
Python+flask+sqlalchemy microservice boilerplate
.PHONY: deps
all: deps
PYTHONPATH=.venv ; . .venv/bin/activate
.venv:
if [ ! -e ".venv/bin/activate_this.py" ] ; then virtualenv --clear .venv ; fi
deps: .venv
PYTHONPATH=.venv ; . .venv/bin/activate && .venv/bin/pip install -U -r requirements.txt
test: deps tests
. .venv/bin/activate && .venv/bin/python -m unittest discover
clean:
rm -rf .venv
rm -rf {*.egg-info,build}
rm -f `find . -name \*.pyc -print0 | xargs -0`
flask
flask-restless
flask-classy
sqlalchemy
Paste
argparse
pyopenssl
requests
formic==0.9beta8
livereload
#!/usr/bin/env python
import os
import sys
import json
import datetime
import logging
__version__ = '1.0.0'
log = logging.getLogger(__name__)
# Activate virtualenv (puts .venv dependencies in path)
if os.path.exists('.venv'): # and __name__ != '__main__':
import inspect
import site
log.info('Activating virtualenv...')
this_path = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
activate_this = os.path.realpath(os.path.join(this_path, '.venv/bin/activate_this.py'))
if not os.path.exists(activate_this):
sys.stderr.write("Missing dependencies. Please run 'make' before running this application.\n")
sys.exit(1)
execfile(activate_this, dict(__file__=activate_this))
site.addsitedir(os.path.join(this_path, '.venv/lib/python2.6/site-packages'))
sys.path.insert(0, this_path) # Unsure if this is even necessary
import argparse
import requests
from flask import Flask, Blueprint, render_template, request, url_for
from flask.ext.classy import FlaskView, route
import flask.ext.restless
import sqlalchemy.ext.declarative
from sqlalchemy import ForeignKey, Column, String, Text, Integer, Boolean, DateTime
from sqlalchemy.orm import backref, relationship
app = application = Flask('myapp')
app.config['DEBUG'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
##############
### Models ###
##############
Model = sqlalchemy.ext.declarative.declarative_base()
class User(Model):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
first_name = Column(String(length=255))
last_name = Column(String(length=255))
email = Column(String(length=255))
bio = Column(Text)
class Address(Model):
__tablename__ = 'addresses'
id = Column(Integer, primary_key=True)
address = Column(String(length=255))
city = Column(String(length=100))
state = Column(String(length=50))
zip_code = Column(String(length=15))
user_id = Column(Integer, ForeignKey(User.id), nullable=False)
user = relationship(User, backref='addresses')
######################
### REST Resources ###
######################
# @app.route('/')
# def index():
# return 'Hello!'
###############
# Boilerplate #
###############
# Initialize database
log.info('Connecting to database...')
engine = sqlalchemy.create_engine(app.config['SQLALCHEMY_DATABASE_URI'], convert_unicode=True)
db_session = sqlalchemy.orm.scoped_session(
sqlalchemy.orm.sessionmaker(autocommit=False,
autoflush=False,
bind=engine))
# Register REST endpoints for SqlAlchemy models at /<tablename>
manager = flask.ext.restless.APIManager(app, session=db_session)
manager.create_api(User, methods=['GET', 'POST', 'DELETE'])
manager.create_api(Address, methods=['GET', 'POST', 'DELETE'])
# Add seed data
def seed_database():
"""
Seeds the database.
"""
log.info('Seeding the database...')
user = User()
user.first_name = 'Chris'
user.last_name = 'Lyon'
user.email = '[email protected]'
db_session.add(user)
address = Address()
address.address = '1234 Main St'
address.city = 'Los Angeles'
address.state = 'CA'
address.zip = '90034'
user.addresses.append(address)
db_session.commit()
@app.teardown_appcontext
def shutdown_session(exception=None):
db_session.remove()
# Bootstrap
if __name__ == '__main__':
default_interface = '0.0.0.0'
default_port = 8000
default_lr_port = 35729
default_env = 'dev'
# Parse arguments
argp = argparse.ArgumentParser(description='Service controller')
argp.add_argument('--interface', '-i', default=default_interface, metavar='IP',
help='IP address to listen on (default: any)')
argp.add_argument('--port', '-p', default=default_port, metavar='PORT',
help='Port to listen on (default: %d)' % default_port)
argp.add_argument('--lr-port', default=default_lr_port, metavar='PORT',
help='Port for LiveReload server to listen on (default: %d)' % default_lr_port)
argp.add_argument('--env', '-e', default=os.environ.get('SERVICE_ENV', default_env), metavar='ENV_NAME',
help='Environment to use (default: SERVICE_ENV or "%s"' % default_env)
argp.add_argument('--ssl', '-s', action='store_true',
help='Enable SSL? If so, looks for X.509 certs in SERVICE_PUB_KEY and SERVICE_PRI_KEY')
argp.add_argument('--init-db', action='store_true',
help='Initialize the database')
args = argp.parse_args()
# Initialize logging
is_dev = (args.env == 'dev')
logging.basicConfig(level=logging.DEBUG if is_dev else logging.INFO,
format='%(asctime)s | %(levelname)-5s | %(name)s: %(message)s')
# Initialize the database
if args.init_db:
log.info('Initializing the database...')
Model.metadata.create_all(bind=engine)
seed_database()
# SSL support needed?
if args.ssl:
from OpenSSL import SSL
ssl_context = SSL.Context(SSL.SSLv23_METHOD)
ssl_context.use_publickey_file(os.environ['SERVICE_PUB_KEY'])
ssl_context.use_privatekey_file(os.environ['SERVICE_PRI_KEY'])
else:
ssl_context = None
# Start web server
log.info('Starting HTTP server on %s:%d...' % (args.interface, args.port))
if is_dev:
import formic
import livereload
# LiveReload HTTP+TCP server (watches files for changes)
lr_server = livereload.Server(app.wsgi_app)
for path in formic.FileSet(directory='.', include=['**.py', '**.conf']):
lr_server.watch(path)
log.info('LiveReload listening on port %d' % args.lr_port)
lr_server.serve(host=args.interface,
port=args.port,
liveport=args.lr_port)
else:
import paste.httpserver
# Paste HTTP server (thread pool and handles load better)
paste.httpserver.serve(app,
host=args.interface,
port=args.port,
ssl_context=ssl_context,
use_threadpool=True)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment