HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonflaskMinor

Multi-dictionary API using Python Flask-RESTful

Submitted by: @import:stackexchange-codereview··
0
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:

/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/:keyword


To 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/:keyword


However, 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 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 as

if __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.