patternpythonflaskMinor
Multi-dictionary API using Python Flask-RESTful
Viewed 0 times
flaskmultipythonusingrestfuldictionaryapi
Problem
I'm working on a simple online dictionary tool, using Python Flask-RESTful as the RESTful API backend. The dictionary tool is modular, it can handle multiple dictionaries, implemented as independent plugins.
I have API endpoints like this:
To make this work in Flask, I have to associate these endpoints with classes that extend the Resource base class, and implement a
I have multiple dictionaries, discovered dynamically from plugins. As they implement the same interface, it would make sense to have a class that can be constructed with any dictionary that implements the common interface, and have the class delegate queries to its dictionary. For example I have a class named
However, Flask's
```
#!/usr/bin/env python
from flask import Flask, render_template, jsonify
from flask.ext.restful import Resource, Api, reqparse
from dictionary.base import lazy_property
from util import discover_dictionaries
app = Flask(__name__)
api = Api(app)
parser = reqparse.RequestParser()
parser.add_argument('similar', type=bool, help='Try to find similar matches when there are no exact')
parser.add_argument('list', type=bool, help='Show list of matches instead of content')
dictionaries = [_ for _ in discover_dictionaries()]
class DictionaryResource(Resource):
@lazy_property
def args(self):
return parser.parse_args()
@property
def dict_id(self):
"""Dynamically
I have API endpoints like this:
/api/v1/dictionaries/:dict_id/find/exact/:keyword
/api/v1/dictionaries/:dict_id/find/prefix/:keyword
/api/v1/dictionaries/:dict_id/find/suffix/:keyword
/api/v1/dictionaries/:dict_id/find/partial/:keywordTo make this work in Flask, I have to associate these endpoints with classes that extend the Resource base class, and implement a
.get method.I have multiple dictionaries, discovered dynamically from plugins. As they implement the same interface, it would make sense to have a class that can be constructed with any dictionary that implements the common interface, and have the class delegate queries to its dictionary. For example I have a class named
FindExact, and I planned to create one instance of it per dictionary to handle the endpoints:/api/v1/dictionaries/dict1/find/exact/:keyword
/api/v1/dictionaries/dict2/find/exact/:keyword
/api/v1/dictionaries/dict3/find/exact/:keywordHowever, Flask's
api.add_resource method takes a class as parameter, not an object. To work around this, I'm creating classes dynamically:```
#!/usr/bin/env python
from flask import Flask, render_template, jsonify
from flask.ext.restful import Resource, Api, reqparse
from dictionary.base import lazy_property
from util import discover_dictionaries
app = Flask(__name__)
api = Api(app)
parser = reqparse.RequestParser()
parser.add_argument('similar', type=bool, help='Try to find similar matches when there are no exact')
parser.add_argument('list', type=bool, help='Show list of matches instead of content')
dictionaries = [_ for _ in discover_dictionaries()]
class DictionaryResource(Resource):
@lazy_property
def args(self):
return parser.parse_args()
@property
def dict_id(self):
"""Dynamically
Solution
You don't need dynamic type creation
I don't quite understand why you're creating new types dynamically. I think you just need to define a constructor for your
Then you don't need a unique type for each dictionary, and you can get rid of the dynamic type creation. You could also use
Some other minor considerations:
I'm not sure why you're doing
instead of just
I'm even more confused by the temporary list because you only use it in the
Otherwise, by making the intermediate list, it seems that you're defeating the point of
Also, instead of putting all of this in a single module I think I'd like it more if you used
If you really didn't want to call
I don't quite understand why you're creating new types dynamically. I think you just need to define a constructor for your
FindExact or DictionaryResource classes, and then pass the keyword argument resource_class_args to add_resource:resource_class_args (tuple) – args to be forwarded to the constructor of the resource.Then you don't need a unique type for each dictionary, and you can get rid of the dynamic type creation. You could also use
resource_class_kwargs if you would prefer that.resource_class_kwargs (dict) – kwargs to be forwarded to the constructor of the resource.Some other minor considerations:
I'm not sure why you're doing
dictionaries = [_ for _ in discover_dictionaries()]instead of just
dictionaries = list(discover_dictionaries())I'm even more confused by the temporary list because you only use it in the
register_dictionary_endpoints function - can't you just change that to this?for dict_id, dictionary in discover_dictionaries():
add_resource(FindExact, '{0}/{1}/find/exact/', dict_id, dictionary)Otherwise, by making the intermediate list, it seems that you're defeating the point of
discover_dictionaries being a generator.Also, instead of putting all of this in a single module I think I'd like it more if you used
init_app and put all of your API code into another module. from flask import Flask
from my_api import api
app = Flask(__name__)
api.init_app(app)
if __name__ == '__main__':
app.run()If you really didn't want to call
register_dictionary_endpoints except in the case of __main__, then I think you could alter the function signature of that and add_resource to take an Api object as their first parameter, and then call it asif __name__ == '__main__':
register_dictionary_endpoints(api)
app.run()Code Snippets
dictionaries = [_ for _ in discover_dictionaries()]dictionaries = list(discover_dictionaries())for dict_id, dictionary in discover_dictionaries():
add_resource(FindExact, '{0}/{1}/find/exact/<string:keyword>', dict_id, dictionary)from flask import Flask
from my_api import api
app = Flask(__name__)
api.init_app(app)
if __name__ == '__main__':
app.run()if __name__ == '__main__':
register_dictionary_endpoints(api)
app.run()Context
StackExchange Code Review Q#60106, answer score: 5
Revisions (0)
No revisions yet.