Skip to content

Commit

Permalink
Merge pull request #11 from JoMingyu/issue/4
Browse files Browse the repository at this point in the history
Flask Large Application Example 적용
  • Loading branch information
JoMingyu committed Feb 23, 2019
2 parents 9cb014c + e3c6e86 commit f43ebe7
Show file tree
Hide file tree
Showing 21 changed files with 169 additions and 9 deletions.
39 changes: 39 additions & 0 deletions app/__init__.py
@@ -0,0 +1,39 @@
from flask import Flask
from werkzeug.exceptions import HTTPException

from app.misc.log import log


def register_extensions(flask_app: Flask):
pass


def register_views(flask_app: Flask):
from app.views import route

route(flask_app)


def register_hooks(flask_app: Flask):
from app.hooks.error import broad_exception_handler, http_exception_handler
from app.hooks.request_context import after_request

flask_app.after_request(after_request)
flask_app.register_error_handler(HTTPException, http_exception_handler)
flask_app.register_error_handler(Exception, broad_exception_handler)


def create_app(*config_cls) -> Flask:
log(message='Flask application initialized with {}'.format(', '.join([config.__name__ for config in config_cls])),
keyword='INFO')

flask_app = Flask(__name__)

for config in config_cls:
flask_app.config.from_object(config)

register_extensions(flask_app)
register_views(flask_app)
register_hooks(flask_app)

return flask_app
18 changes: 18 additions & 0 deletions app/context.py
@@ -0,0 +1,18 @@
from flask import current_app, request


class _ContextProperty:
@property
def secret_key(self) -> str:
return current_app.secret_key

@property
def user_agent(self) -> str:
return request.headers['user_agent']

@property
def remote_addr(self) -> str:
return request.remote_addr


context_property = _ContextProperty()
Empty file added app/decorators/__init__.py
Empty file.
Empty file added app/extensions.py
Empty file.
Empty file added app/hooks/__init__.py
Empty file.
9 changes: 9 additions & 0 deletions app/hooks/error.py
@@ -0,0 +1,9 @@
from werkzeug.exceptions import HTTPException


def http_exception_handler(e: HTTPException):
return '', e.code


def broad_exception_handler(e: Exception):
return '', 500
9 changes: 9 additions & 0 deletions app/hooks/request_context.py
@@ -0,0 +1,9 @@
from flask import Response


def after_request(response: Response) -> Response:
try:
response.headers['X-Content-Type-Options'] = 'nosniff'
response.headers['X-Frame-Options'] = 'deny'
finally:
return response
Empty file added app/misc/__init__.py
Empty file.
12 changes: 12 additions & 0 deletions app/misc/log.py
@@ -0,0 +1,12 @@
from termcolor import colored


def log(message: str, keyword: str="WARN"):
if keyword == "WARN":
print(colored('[WARN]', 'yellow'), message)
elif keyword == "ERROR":
print(colored('[ERROR] ' + message, 'red'))
elif keyword == "INFO":
print(colored('[INFO]', 'blue'), message)
else:
print(colored('[{}]'.format(type), 'cyan'), message)
Empty file added app/models/__init__.py
Empty file.
25 changes: 25 additions & 0 deletions app/views/__init__.py
@@ -0,0 +1,25 @@
from flask import Blueprint, Flask
from flask_restful import Api


def route(flask_app: Flask):
from app.views.sample import sample

handle_exception_func = flask_app.handle_exception
handle_user_exception_func = flask_app.handle_user_exception
# register_blueprint 시 defer되었던 함수들이 호출되며, flask-restful.Api._init_app()이 호출되는데
# 해당 메소드가 app 객체의 에러 핸들러를 오버라이딩해서, 별도로 적용한 handler의 HTTPException 관련 로직이 동작하지 않음
# 따라서 두 함수를 임시 저장해 두고, register_blueprint 이후 함수를 재할당하도록 함

# - blueprint, api object initialize
api_v1_blueprint = Blueprint('api_v1', __name__, url_prefix='/api/v1')
api_v1 = Api(api_v1_blueprint)

# - route
api_v1.add_resource(sample.Sample, '/sample')

# - register blueprint
flask_app.register_blueprint(api_v1_blueprint)

flask_app.handle_exception = handle_exception_func
flask_app.handle_user_exception = handle_user_exception_func
10 changes: 10 additions & 0 deletions app/views/base.py
@@ -0,0 +1,10 @@
import arrow
from flask_restful import Resource


class BaseResource(Resource):
def __init__(self):
self.utcnow = arrow.utcnow()
self.kstnow = self.utcnow.to('Asia/Seoul')
self.iso8601_formatted_utcnow = self.utcnow.isoformat()
self.iso8601_formatted_kstnow = self.kstnow.isoformat()
Empty file added app/views/sample/__init__.py
Empty file.
10 changes: 10 additions & 0 deletions app/views/sample/sample.py
@@ -0,0 +1,10 @@
from flask import request

from app.views.base import BaseResource


class Sample(BaseResource):
def post(self):
payload = request.json

return payload, 201
Empty file added config/__init__.py
Empty file.
9 changes: 9 additions & 0 deletions config/app_config.py
@@ -0,0 +1,9 @@
import os


class LocalLevelConfig:
SECRET_KEY = os.getenv('SECRET_KEY', '85c145a16bd6f6e1f3e104ca78c6a102')


class ProductionLevelConfig:
SECRET_KEY = os.getenv('SECRET_KEY')
6 changes: 6 additions & 0 deletions config/db_config.py
@@ -0,0 +1,6 @@
class LocalDBConfig:
pass


class RemoteDBConfig:
pass
Empty file added constants/__init__.py
Empty file.
6 changes: 6 additions & 0 deletions constants/local_run.py
@@ -0,0 +1,6 @@
RUN_SETTING = {
'host': 'localhost',
'port': 5000,
'debug': True,
'threaded': True
}
10 changes: 10 additions & 0 deletions production_app.py
@@ -0,0 +1,10 @@
import os

from app import create_app
from config.app_config import ProductionLevelConfig
from config.db_config import RemoteDBConfig

if 'SECRET_KEY' not in os.environ:
raise Warning('The secret key must be passed by the <SECRET_KEY> envvar.')

application = create_app(ProductionLevelConfig, RemoteDBConfig)
15 changes: 6 additions & 9 deletions run.py
@@ -1,12 +1,9 @@
from flask import Flask

app = Flask(__name__)


@app.route('/')
def index():
return 'This is version 2'
from app import create_app
from config.app_config import LocalLevelConfig
from config.db_config import LocalDBConfig
from constants.local_run import RUN_SETTING

app = create_app(LocalLevelConfig, LocalDBConfig)

if __name__ == '__main__':
app.run()
app.run(**RUN_SETTING)

0 comments on commit f43ebe7

Please sign in to comment.