From d24cb425f76afca110b4a72f5c2cb92ce5969674 Mon Sep 17 00:00:00 2001 From: Greg Hill Date: Sat, 27 Aug 2016 13:49:42 -0500 Subject: [PATCH] Make the dasherize behavior optional The spec doesn't require it, although it is recommended. I personally don't want the behavior because I want my client attributes to match those in the database. Fixes #24 --- README.md | 5 ++++- setup.py | 2 +- sqlalchemy_jsonapi/flaskext.py | 18 ++++++++++------ sqlalchemy_jsonapi/serializer.py | 24 +++++++++++++++++---- sqlalchemy_jsonapi/tests/test_serializer.py | 11 ++++++++++ 5 files changed, 48 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 476c826..e506ca1 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,9 @@ api = FlaskJSONAPI(app, db) # Or, for factory-style applications api = FlaskJSONAPI() api.init_app(app, db) + +# To disable using hyphens as word-separators, disable the dasherize option +api = FlaskJSONAPI(app, db, options={'dasherize': False}) ``` ## Quick usage without Flask @@ -38,4 +41,4 @@ api = JSONAPI(Base) # And assuming a SQLAlchemy session print(api.get_collection(session, {}, 'resource-type')) -``` \ No newline at end of file +``` diff --git a/setup.py b/setup.py index 4f72f56..53209a0 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ requirements.append('enum34') setup(name='SQLAlchemy-JSONAPI', - version='4.0.9', + version='4.0.10', url='http://github.com/coltonprovias/sqlalchemy-jsonapi', license='MIT', author='Colton J. Provias', diff --git a/sqlalchemy_jsonapi/flaskext.py b/sqlalchemy_jsonapi/flaskext.py index 2548d32..7dc480a 100644 --- a/sqlalchemy_jsonapi/flaskext.py +++ b/sqlalchemy_jsonapi/flaskext.py @@ -85,7 +85,8 @@ def __init__(self, app=None, sqla=None, namespace='api', - route_prefix='/api'): + route_prefix='/api', + options=None): """ Initialize the adapter. If app isn't passed here, it should be passed in init_app. @@ -100,9 +101,10 @@ def __init__(self, self._handler_chains = dict() if app is not None: - self._setup_adapter(namespace, route_prefix) + self._setup_adapter(namespace, route_prefix, options=options) - def init_app(self, app, sqla, namespace='api', route_prefix='/api'): + def init_app(self, app, sqla, namespace='api', route_prefix='/api', + options=None): """ Initialize the adapter if it hasn't already been initialized. @@ -114,7 +116,7 @@ def init_app(self, app, sqla, namespace='api', route_prefix='/api'): self.app = app self.sqla = sqla - self._setup_adapter(namespace, route_prefix) + self._setup_adapter(namespace, route_prefix, options=options) def wrap_handler(self, api_types, methods, endpoints): """ @@ -156,14 +158,18 @@ def wrapped(*args, **kwargs): return wrapped - def _setup_adapter(self, namespace, route_prefix): + def _setup_adapter(self, namespace, route_prefix, options=None): """ Initialize the serializer and loop through the views to generate them. :param namespace: Prefix for generated endpoints :param route_prefix: Prefix for route patterns """ - self.serializer = JSONAPI(self.sqla.Model, prefix='{}://{}{}'.format(self.app.config['PREFERRED_URL_SCHEME'], self.app.config['SERVER_NAME'], route_prefix)) + self.serializer = JSONAPI(self.sqla.Model, + prefix='{}://{}{}'.format(self.app.config['PREFERRED_URL_SCHEME'], + self.app.config['SERVER_NAME'], + route_prefix), + options=options) for view in views: method, endpoint = view pattern = route_prefix + endpoint.value diff --git a/sqlalchemy_jsonapi/serializer.py b/sqlalchemy_jsonapi/serializer.py index 16dcef1..c534686 100644 --- a/sqlalchemy_jsonapi/serializer.py +++ b/sqlalchemy_jsonapi/serializer.py @@ -212,16 +212,27 @@ def get_rel_desc(instance, key, action): class JSONAPI(object): """ JSON API Serializer for SQLAlchemy ORM models. """ - def __init__(self, base, prefix=''): + default_options = { + 'dasherize': True, + } + + def __init__(self, base, prefix='', options=None): """ Initialize the serializer. :param base: Declarative base instance :param namespace: The namespace of the API endpoint + :param options: an options dict (refer to `JSONAPI.default_options` + for available settings and their defaults) """ + self.base = base self.prefix = prefix self.models = {} + self.options = dict(**self.default_options) + if options: + self.options.update(options) + for name, model in base._decl_class_registry.items(): if name.startswith('_'): continue @@ -236,8 +247,8 @@ def __init__(self, base, prefix=''): model.__jsonapi_rel_desc__ = {} model.__jsonapi_permissions__ = {} model.__jsonapi_type__ = api_type - model.__jsonapi_map_to_py__ = {dasherize(underscore(x)): x for x in model_keys} - model.__jsonapi_map_to_api__ = {x: dasherize(underscore(x)) for x in model_keys} + model.__jsonapi_map_to_py__ = {self._dasherize(underscore(x)): x for x in model_keys} + model.__jsonapi_map_to_api__ = {x: self._dasherize(underscore(x)) for x in model_keys} for prop_name, prop_value in iterate_attributes(model): @@ -282,8 +293,13 @@ def __init__(self, base, prefix=''): perm_idv[check_perm] = prop_value self.models[model.__jsonapi_type__] = model + def _dasherize(self, word): + if self.options.get('dasherize', True): + return dasherize(word) + return word + def _api_type_for_model(self, model): - return dasherize(tableize(model.__name__)) + return self._dasherize(tableize(model.__name__)) def _fetch_model(self, api_type): if api_type not in self.models.keys(): diff --git a/sqlalchemy_jsonapi/tests/test_serializer.py b/sqlalchemy_jsonapi/tests/test_serializer.py index 07060c9..9cfa481 100644 --- a/sqlalchemy_jsonapi/tests/test_serializer.py +++ b/sqlalchemy_jsonapi/tests/test_serializer.py @@ -1,4 +1,5 @@ from app import api +from sqlalchemy_jsonapi import JSONAPI import uuid @@ -9,3 +10,13 @@ def test_include_different_types_same_id(session, comment): r = api.serializer.get_resource(session, {'include': 'post,author'}, 'blog-comments', comment.id) assert len(r.data['included']) == 2 + + +def test_no_dasherize(session, comment): + api.serializer = JSONAPI(api.serializer.base, api.serializer.prefix, + options={'dasherize': False}) + + r = api.serializer.get_resource(session, {}, 'blog_comments', comment.id) + assert r.data['data']['type'] == 'blog_comments' + + api.serializer = JSONAPI(api.serializer.base, api.serializer.prefix)