Flask - avoiding having code in controller - python

I'm learning Flask and all the router examples I see look like this:
#app.route('/')
def hello():
name = request.args.get("name", "World")
return f'Hello, {escape(name)}!'
I don't want any code in my controller except:
#app.route('/')
def hello():
mycode.doHelloWorld()
return mycode.sayHelloWorld()
I like my code to be reusable and testable and if it's in the Flask controller it's neither.
Can someone point me to examples of using Flask with the business logic/code separate from the controller?

You can try defining a function which returns a similar output given a request parameter like so:
def sayHelloWorld(request):
name = request.args.get("name", "World")
return f"Hello, {name}!"
#app.route("/")
def hello():
return sayHelloWorld(request)
or based on the code you provided, you can create a class which contains reusable code snippets as static functions
class ReusableCode:
#staticmethod
def sayHelloWorld(request):
name = request.args.get("name", "World")
return f"Hello, {name}!"
#app.route("/"):
def hello():
return ReusableCode.sayHelloWorld(request)

Related

Associate methods of an object to API URL routes with Python Flask

I want to associate a few methods of a (real world) object to Flask URL routes:
class Door:
def __init__(self, location):
pass
def open(self):
pass
def close(self):
pass
def openlater(self, waitseconds=2):
pass
d = Door(location="kitchen")
app.add_url_rule("/door/open", view_func=lambda: d.open())
app.add_url_rule("/door/close", view_func=lambda: d.close())
app.add_url_rule("/door/openlater", view_func=lambda: d.openlater(waitseconds=10))
# or (if we don't override optional parameters):
for func in ['open', 'close', 'openlater']:
app.add_url_rule(f"/door/{func}", view_func=lambda: getattr(d, func)())
But this does not work:
AssertionError: View function mapping is overwriting an existing endpoint function:
How to do this properly with Python Flask?
Note:
I could do
app.add_url_rule(f"/door/{func}", view_func=getattr(d, func))
but then I don't have any lambda anymore and I cannot set parameters.
To create views and map them to a URL, you should create routes. Do this the following way:
#app.route('/')
def index():
return "Hello World!"
Now when you access your webpage you get Hello World on screen. The add_url_rule is hardly use anymore.
If you want your URL to be dynamic you can do the following:
#app.route('/func/<func>')
def index(func):
return "Hello World!"

Flask can a route inherit code from another route?

Is it possible for a flask app route to inherit code from another flask app route?
Example
#app.route('/')
def home():
# some code
#app.route('/something')
def something():
# inherit code from home function
You could probably write a handler that is called by both functions like this:
#app.route('/')
def home():
shared_code()
#app.route('something')
def something():
shared_code()
# other specific code
def shared_code():
print("this is called by both routes")

Is it possible to dynamically set the name of a decorator in python?

So, I was studying the FLASK framework in python and made my very first simple program and by default I've seen people using app = Flask(__name__) and just below that line they use the decorator #app.route("/"),So I thought that what would happen if I change the name of the variable to something else? Like in the code below I've changed it to something = Flask(__name__) so now I'm confused that how does it still work when I decorate the function index() with #something.route("/") ,is the name of the decorator function defined in FLASK changing dynamically? and if so how can I make my own decorators like this so that they too change their names dynamically?
from flask import Flask
something = Flask(__name__)
#something.route("/")
def index():
return "Hello, World!"
Decorator is just a syntactic sugar:
def decorator(func):
pass
#decorator
def decorated():
pass
is the same as:
def decorator(func):
pass
def decorated():
pass
decorated = decorator(decorated)
The decorator name is nothing more than a function that accepts one argument. You could even use print function as a decorator. Any valid callable will do:
#print
def index():
pass
Obviously that makes little sense, because:
def index():
pass
index = print(index)
Anyway that's how this could be implemented in flask
class Flask:
def route(self, url):
def wrapper(func):
# register route for url
return func
return wrapper
something = Flask()
#something.route("/")
def index():
pass
something.route("/") is a function call that returns the real decorator which is actually the inner function named wrapper.
So you can even do something like this:
something_route_index = something.route("/")
#something_route_index
def index():
pass

Using routes in classes

I am trying to rewrite some working code as a class.
A minimal working code:
from flask import Flask
app = Flask(__name__)
#app.route("/1")
def func_1():
return "view 1"
#app.route("/2")
def func_2():
return "view 2"
app.run()
How to write it as a class with the route defined during the object instantiation?
I only want it clean: after instantiating an object I want the respective route already working with no extra lines of code.
This is the closest I get to:
from flask import Flask
class NewView:
def __init__(self, url, string):
self.string = string
self.server = Flask(__name__)
self.server.add_url_rule(url, 'index', self.index)
def index(self):
return self.string
v1 = NewView("/1", "view 1")
v2 = NewView("/2", "view 2")
v1.server.run()
This, of course, recognizes /1 as route for v1.index(), but /2 doesn't work.
The ideal would be something like the following but I cannot make it work:
from flask import Flask
app = Flask(__name__)
class NewView:
def __init__(self, url, string):
....
app.add_url_rule(url, ...?..., self.index)
def index(self):
return self.string
v1 = NewView("/1", "view 1")
v2 = NewView("/2", "view 2")
app.run()
First, your mistake:
def __init__(self, url, string):
self.string = string
self.server = Flask(__name__)
self.server.add_url_rule(url, 'index', self.index)
Because you have two instances of this class, there are two Flask objects. You only run the second one.
What you're immediately trying to do can be done like this:
import flask
# There can be only one!
app = flask.Flask(__name__)
class MyView:
def __init__(self, url, name, string):
self.url = url
self.string = string
app.add_url_rule(url, name, self.serve)
def serve(self):
return self.string
view1 = MyView('/1', name='view1', string='This is View 1.')
view2 = MyView('/2', name='view2', string='This is View 2, not view 1.')
app.run()
The above code will work and do what you expect. Something to note is that, since Flask likes names for unique routes, I have you passing in a name for each route. That way, url_for('view1') and url_for('view2') work.
Having said all that, the community has largely already accomplished much of this Pluggable Views. Check it out.
I think that if your goal is to keep code clean, you should avoid creating objects that are never used. The class based views in flask. The as_view() method that is being passes is class method, so also here there is no need to create a never used object. The process of registring urls belongs to creation of an app, not separate objects (that's how it works in Django for instance). If I were you I would go with something similar to this:
from flask import Flask
from flask.views import View
def init_app():
app = Flask(__name__)
app.add_url_rule('/', view_func=NewView.as_view('index'))
return app
class NewView(View):
def dispatch_request(self):
return 'Test'
app = init_app()
app.run()

flask sub function not yielding results

I have a bunch of code (1300 lines) that is working correctly and I am trying to incorporate flask into the picture. In order to do this, I an trying to use flask.Response to call a function within my method, that calls another method in my class.
Here is test code that re-creates my problem.
#!/usr/bin/env python
import flask
class TestClass(object):
app = flask.Flask(__name__)
def __init__(self):
pass
def worker(self):
yield 'print test\n'
#app.route('/')
def test_method_get_stuff():
return flask.render_template('index.html')
#app.route('/', methods=['POST'])
def test_method_post_stuff():
def test_method_sub_function():
tc.worker()
return flask.Response(test_method_sub_function(),mimetype= 'text/plain')
tc = TestClass()
tc.app.run(debug=True)
index.html just has a text box with a submit button.
The issue I have is once you click the submit button, the request goes through sucessfully but the page is blank with no errors in the python command line or in the browser, and what I expect to happen is to show in plain text "print test" with a newline.'
Any assistance would be appreciated. I am trying to avoid completely re-writing all my code. With the understanding that i will have to replace 'print' with 'yield' commands in my code.
Your nested test_method_sub_function() function doesn't return anything; it simply creates the generator (by calling a generator function), then exits.
It should at the very least return the tc.worker() call:
def test_method_sub_function():
return tc.worker()
at which point the route works. You may as well skip this nested function however and use tc.worker() directly:
#app.route('/', methods=['POST'])
def test_method_post_stuff():
return flask.Response(tc.worker(), mimetype='text/plain')
One note: although your use of the Flask object as a class attribute happens to work, you should to put it in a class. Leave the app object and routes outside of the class:
import flask
class TestClass(object):
def worker(self):
yield 'print test\n'
tc = TestClass()
app = flask.Flask(__name__)
#app.route('/')
def test_method_get_stuff():
return flask.render_template('index.html')
#app.route('/', methods=['POST'])
def test_method_post_stuff():
return flask.Response(tc.worker(), mimetype='text/plain')
app.run(debug=True)

Categories

Resources