Flaskの派生のひとつでRESTサービスを簡単に構築できるEveというのがある。スキーマの設計さえしておけば、SiteRootにREST APIを展開してくれる優れものだが、Django+REST Framework+angularのような感じで、構築しようとしたときに結構はまったので記録に残しておく。
##REST API Rootの変更
Eveの場合通常API Root=Rootとなるので、rootアクセス時にindex.html表示なんてことができない。
そこで、
URL_PREFIX = 'api'
みたく、prefixを定義してやることで、APIルートを変更できる。
##Flask とEveの設定
EveとFlaskで別々に設定がある。
Eveは指定しないと同じディレクトリのsettings.pyを読みに行く。
別途読み込みたい場合は
app=EVE(settings='hoge.py')
と書くとそちらを読みに行く。ただし、settingsはdictとfileしか受け付けない。
Eveが使用している設定以外はsettings.pyに書いても機能しないはず・・・
Flaskの設定は切り離して、create_app()などのappFactoryで与える必要がある。
def create_app(api_settings=None, flask_config='oss_app.config', package_name=__name__, flask_conf_override=None):
# Define the WSGI application object
if api_settings is not None:
app = Eve(settings=api_settings, import_name=__name__)
else:
app = Eve()
# Configurations
app.config.from_object(flask_config)
app.config.from_object(flask_conf_override)
##static path
Eveの場合、無設定で/staticをたたくと、404が返ってくる。
理由はeveのパッケージのflaskapp.pyで
class Eve(Flask, Events):
中略
def __init__(self, import_name=__package__, settings='settings.py',
validator=Validator, data=Mongo, auth=None, redis=None,
url_converters=None, json_encoder=None,
media=GridFSMediaStorage, **kwargs):
となっている。
Flaskはstatic_root_pathを探す時に、import_nameがあるとimport_nameを探すので、import_name=package=EVEとなり、site-package内のEVEディレクトリがsatticfileのディレクトリと認識してしまい、404が返ってしまう。
対策はEveインスタンス生成時にimport_name=__name__としてやること。
app=Eve(import_name=__name__)
##Eve-mongoengineを使う。
Eveの場合、初期状態はpymongoを使用する、ORMを使用したい場合、Eve-mongoengineが使える。ただし、1年ほど更新が止まっているためか、使用すると起動シークエンスで_etag errorとなり落ちる。
EveのどこかのバージョンからEtagあが必須になったのだが追い付いていないのだと思う。
これにについては
eve-mongoengine\_init.pyの102行目あたりと190行目あたりにEtagの定義追加で何とかなる。
91:
def _parse_config(self):
# parse app config
config = self.app.config
try:
self.last_updated = config['LAST_UPDATED']
except KeyError:
self.last_updated = '_updated'
try:
self.date_created = config['DATE_CREATED']
except KeyError:
self.date_created = '_created'
+ try:
+ self.etag = config['ETAG']
+ except KeyError:
+ self.etag = '_etag'
def fix_model_class(self, model_cls):
中略
192:
date_field_cls = mongoengine.DateTimeField
+ string_field_cls = mongoengine.StringField
# field names have to be non-prefixed
last_updated_field_name = self.last_updated.lstrip('_')
+ date_created_field_name = self.date_created.lstrip('_')
etag__field_name = self.etag.lstrip('_')
new_fields = {
# TODO: updating last_updated field every time when saved
last_updated_field_name: date_field_cls(db_field=self.last_updated,
default=get_utc_time),
date_created_field_name: date_field_cls(db_field=self.date_created,
default=get_utc_time),
etag__field_name: string_field_cls(db_field=self.etag,
default=""),
}