Using routes in classes - python

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()

Related

Using Flask whit class - Python [duplicate]

I'm learning Flask and am a bit confused about how to structure my code. So I tried to extend Flask main class as follows:
from flask import Flask, ...
class App(Flask):
def __init__(self, import_name, *args, **kwargs):
super(App, self).__init__(import_name, *args, **kwargs)
Note that I am aware of that this may be a completely wrong approach.
So that when I want to start the app I do:
app = App(__name__)
if __name__ == '__main__':
app.run()
This way I can order my methods and routes in the class, but the problem is when using self-decorators:
#route('/')
def home(self, context=None):
context = context or dict()
return render_template('home.html', **context)
Which raises an error as unresolved reference 'route'. I guess this is not the way I should be structuring the app. How should I do it instead or how do I get the error fixed?
Doing this doesn't make sense. You would subclass Flask to change its internal behavior, not to define your routes as class methods.
Instead, you're looking for blueprints and the app factory pattern. Blueprints divide your views into groups without requiring an app, and the factory creates and sets up the app only when called.
my_app/users/__init__.py
from flask import Blueprint
bp = Blueprint('users', __name__, url_prefix='/users')
my_app/users/views.py
from flask import render_template
from my_app.users import bp
#bp.route('/')
def index():
return render_template('users/index.html')
my_app/__init__.py
def create_app():
app = Flask(__name__)
# set up the app here
# for example, register a blueprint
from my_app.users import bp
app.register_blueprint(bp)
return app
run.py
from my_app import create_app
app = create_app()
Run the dev server with:
FLASK_APP=run.py
FLASK_DEBUG=True
flask run
If you need access to the app in a view, use current_app, just like request gives access to the request in the view.
from flask import current_app
from itsdangerous import URLSafeSerializer
#bp.route('/token')
def token():
s = URLSafeSerializer(current_app.secret_key)
return s.dumps('secret')
If you really want to define routes as methods of a Flask subclass, you'll need to use self.add_url_rule in __init__ rather than decorating each route locally.
class MyFlask(Flask):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.add_url_rule('/', view_func=self.index)
def index(self):
return render_template('index.html')
The reason route (and self) won't work is because it's an instance method, but you don't have an instance when you're defining the class.

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 cant initial my class when rendering the template

I use Flask:
from flask import Flask, render_template, session
I want to package some session operation methods in a class like this:
class Session:
def __init__(self):
self.ip = request.remote_addr
self.name = request.remote_user
session['ip'] = self.ip
session['name'] = self.name
in order to render these two attributes in templates by the function below:
#app.route('/', methods=['GET'], strict_slashes=False)
def indexpage():
return render_template('index.html',s=Session())
However, different devices get the same output which means Session.ip and Session.name are the same from different devices. I think class Session is not initialized properly.

Flask - avoiding having code in controller

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)

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