24

I'm using Flask to expose some data-crunching code as a web service. I'd like to have some class variables that my Flask functions can access.

Let me walk you through where I'm stuck:

from flask import Flask
app = Flask(__name__)

class MyServer:
  def __init__(self):
    globalData = json.load(filename)

  @app.route('/getSomeData')
  def getSomeData():
    return random.choice(globalData) #select some random data to return

if __name__ == "__main__":
  app.run(host='0.0.0.0')

When I run getSomeData() outside of Flask, it works fine. But, when I run this with Flask, I get 500 internal server error. There's no magic here, and Flask has no idea that it's supposed to initialize a MyServer object. How can I feed an instance of MyServer to the app.run() command?

I could admit defeat and put globalData into a database instead. But, is there an other way?

0

7 Answers 7

17

You can create an instance of MyServer just outside the scope of your endpoints and access its attributes. This worked for me:

class MyServer:
    def __init__(self):
        self.globalData = "hello"

from flask import Flask
app = Flask(__name__)

my_server = MyServer()

@app.route("/getSomeData")
def getSomeData():
    return my_server.globalData

if __name__ == "__main__":
    app.run(host="0.0.0.0")
Sign up to request clarification or add additional context in comments.

1 Comment

This is a decent alternative, but you still can't write end-to-end tests AND mock out dependencies (like network or filesystem IO) because your dependencies are not injected. You can do something hacky like unittest.mock.patch, but then you have to take care to construct your real server the same way you construct your test server (except for the mocks, of course). This is basically the Singleton antipattern
10

I know this is a late reply, but I came across this question while facing a similar issue. I found flask-classful really good. You inherit your class from FlaskView and register the Flask app with your MyServer class

http://flask-classful.teracy.org/#

In this case, with flask-classful, your code would look like this:

from flask import Flask
from flask_classful import FlaskView, route

app = Flask(__name__)

class MyServer(FlaskView):
  def __init__(self):
    globalData = json.load(filename)

  @route('/getSomeData')
  def getSomeData():
    return random.choice(globalData) #select some random data to return


MyServer.register(app, base_route="/")


if __name__ == "__main__":
  app.run(host='0.0.0.0')

1 Comment

Please replace flask-classful with flask_classful (see github.com/teracyhq/flask-classful )
6

The least-coupled solution is to apply the routes at runtime (instead of at load time):

def init_app(flask_app, database_interface, filesystem_interface):
    server = MyServer(database_interface, filesystem_interface)
    flask_app.route('get_data', methods=['GET'])(server.get_data)

This is very testable--just invoke init_app() in your test code with the mocked/faked dependencies (database_interface and filesystem_interface) and a flask app that has been configured for testing (app.config["TESTING"]=True or something like that) and you're all-set to write tests that cover your entire application (including the flask routing).

The only downside is this isn't very "Flasky" (or so I've been told); the Flask idiom is to use @app.route(), which is applied at load time and is necessarily tightly coupled because dependencies are hard-coded into the implementation instead of injected into some constructor or factory method (and thus complicated to test).

1 Comment

Flask passes in server.get_data the instance of MyServer, but where's the request data?
3

The following code is a simple solution for OOP with Flask:

from flask import Flask, request


class Server:
    def __init__(self, name):
        self.app = Flask(name)

        @self.app.route('/')
        def __index():
            return self.index()

        @self.app.route('/hello')
        def __hello():
            return self.hello()

        @self.app.route('/user_agent')
        def __user_agent():
            return self.user_agent()

        @self.app.route('/factorial/<n>', methods=['GET'])
        def __factorial(n):
            return self.factorial(n)

    def index(self):
        return 'Index Page'

    def hello(self):
        return 'Hello, World'

    def user_agent(self):
        return request.headers.get('User-Agent')

    def factorial(self, n):
        n = int(n)
        fact = 1
        for num in range(2, n + 1):
            fact = fact * num
        return str(fact)

    def run(self, host, port):
        self.app.run(host=host, port=port)


def main():
    server = Server(__name__)
    server.run(host='0.0.0.0', port=5000)


if __name__ == '__main__':
    main()

To test the code, browse the following urls:

  • http://localhost:5000/
  • http://localhost:5000/hello
  • http://localhost:5000/user_agent
  • http://localhost:5000/factorial/10

2 Comments

I get NameError: name 'self' is not defined on the decorator.
1

a bit late but heres a quick implementation that i use to register routes at init time

from flask import Flask,request,render_template
from functools import partial


registered_routes = {}
def register_route(route=None):
    #simple decorator for class based views
    def inner(fn):
        registered_routes[route] = fn
        return fn
    return inner

class MyServer(Flask):
    def __init__(self,*args,**kwargs):
        if not args:
            kwargs.setdefault('import_name',__name__)
        Flask.__init__(self,*args ,**kwargs)
        # register the routes from the decorator
        for route,fn in registered_routes.items():
            partial_fn = partial(fn,self)
            partial_fn.__name__ = fn.__name__
            self.route(route)(partial_fn)


    @register_route("/")
    def index(self):
        return render_template("my_template.html")

if __name__ == "__main__":
    MyServer(template_folder=os.path.dirname(__file__)).run(debug=True)

Comments

0

if you wish to approach MyServer class as a resource
I believe that flask_restful can help you:

from flask import Flask
from flask_restful import Resource, Api
import json
import numpy as np

app = Flask(__name__)
api = Api(app)

class MyServer(Resource):
    def __init__(self):
        self.globalData = json.load(filename)

    def get(self):
        return np.random.choice(self.globalData)

api.add_resource(MyServer, '/')

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

Comments

0

Here is a simple example that answers with "foo" on http://localhost:5000/, completely contained within a class.

from flask import Flask


class ExampleApp:
    def __init__(self):
        self._app = Flask(__name__)
        self._app.route("/")(self.foo_route)
        self._foo = "foo"

    def run(self):
        self._app.run()

    def foo_route(self):
        return self._foo


if __name__ == "__main__":
    ExampleApp().run()

Instead of using decorators, you call the decorator-function (app.route) with the method you'd like it to use to generate a response.

_foo is used as an example to show that foo_route can indeed access class members.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.