Flask documentation clearly explains how to create a custom error page via the errorhandler() decorator.
How can I use custom pages when my Flask app is part of a class as the decorator cannot be used with a method? An example:
import flask
class Webserver:
def __init__(self):
app = flask.Flask(__name__)
app.add_url_rule('/', view_func=self.hello)
app.run()
def hello(self):
return 'hello'
if __name__ == "__main__":
Webserver()
The usage of add_url_rule() bypasses the same problem for routing, is there an equivalent for errorhandler()?
There is an equivalent for registering error handlers directly, called app.register_error_handler():
class Webserver:
def __init__(self):
app = flask.Flask(__name__)
app.add_url_rule('/', view_func=self.hello)
app.register_error_handler(404, self.not_found_handler)
app.run()
Even so, the errorhandler() just needs to be passed the bound method. If you wanted to register a method as the handler for the 404 Not Found HTTP code, for example, just create the decorator object for 404 and call that with the bound method as the argument:
class Webserver:
def __init__(self):
app = flask.Flask(__name__)
app.add_url_rule('/', view_func=self.hello)
app.errorhandler(404)(self.not_found_handler)
app.run()
This works because all the #app.errorhandler() decorator does is register the callable, so the return value is the original callable still. You can ignore that here and only use it for the registration action.
The same would work for the app.route() decorator.
Related
i am trying to generate Flask route using a basic DI i.e mapping methods as route handlers, i am a total beginner at Flask so mind my basic skills
class myClass():
def __init__(self):
self.dbConnObj = DbToolsMySql('someconnection', 'slave')
self.dbConnObj.connect()
self.blueprint = Blueprint('myClass', __name__)
self.blueprint.add_url_rule('/my_method', view_func=self.my_method)
def my_method(self, event):
retun "hello"
and then in my handler file
from flask import Flask
from flask_restful import Api, Resource
from src.app.services.myClassimport myClass
app = Flask(__name__)
app.register_blueprint(myClass.blueprint)
if __name__ == "main":
app.run()
Quite simple ehh???? but not working... i am getting following message
Not Found The requested URL was not found on the server. If you
entered the URL manually please check your spelling and try again.
typically you add routes to the Flask app with decorators like so:
app = Flask(__name__)
#app.route('/some-endpoint')
def some_endpoint_handler():
# do something
pass
Or you can add without a decorator like so:
def some_endpoint_handler():
# do something
pass
app = Flask(__name__)
app.route('/some-endpoint', methods=['GET'])(some_endpoint_handler)
So in your scenario, you can pass the app.route call to your myClass object and set the route like this:
class myClass():
def __init__(self, router):
self.dbConnObj = DbToolsMySql('someconnection', 'slave')
self.dbConnObj.connect()
self.blueprint = Blueprint('myClass', __name__)
#self.blueprint.add_url_rule('/my_method', view_func=self.my_method)
router('/my_method', ['GET'])(self.my_method)
def my_method(self, event):
retun "hello"
myObj = myClass( app.route )
or, invert the dependency:
app = Flask(__name__)
#app.route(myClass.blueprint.some_endpoint_string)
def some_endpoint_handler():
myClass.blueprint.call_some_endpoint_handler()
pass
if __name__ == "main":
app.run()
I am trying to create a simple Rest API.
Even if it is simple, I don't want to mix everything in a single file.
Therefore I have defined separate classes
Here is some of my files
app = Flask(__name__)
if __name__ == '__main__':
api = PostApi(app)
api.setup()
api.set_routes()
app.run(debug=True)
Post API class
class PostApi(object):
BASE_API_ROUTE = '/post'
def __init__(self, app):
super(PostApi, self).__init__()
self.app = app
def setup(self):
self.api = Api(self.app)
self.app.config['SECRET_KEY'] = SECRET['digest_key']
def set_routes(self):
self.api.add_resource(PostCategories, self.BASE_API_ROUTE + "/categories")
self.api.add_resource(PostCatalog, self.BASE_API_ROUTE + "/catalog")
self.api.add_resource(PostTags, self.BASE_API_ROUTE + "/tags")
And for example one of my endpoint classes
class PostTags(Resource):
def __init__(self):
super(PostTags, self).__init__()
def get(self):
return {'hello': 'world'}
It works, but I need to add authentication for my routes.
As you can see I am not using route decorators like app.route instead I am using the library flask_restful.
I need to protect my routes with the Digest Auth in this case. However, I am not sure how to do this, because I am not using decorators
I am a newbie developer. Could you suggest how to keep my endpoints separated and apply some protection to my routes.
You can use before_request. This will be called before every request on every route.
something like this:
#app.before_request
def before_request():
//add your logic here
there's also before_first_request.
visit Flask Documentation for more info.
I am working on setting up unit tests in my flask project right now. My test file is below:
import flask_testing
import unittest
from flask import Flask
from flask_testing import TestCase
class MyTest(TestCase):
def setUp(self):
pass # Executed before each test
def tearDown(self):
pass # Executed after each test
def create_app(self):
app = Flask(__name__)
# app.config['TESTING'] = True
return app
def test_greeting(self):
response = self.client.get('/')
print("should return 404 on landing page")
self.assertTemplateUsed('index.html')
self.assertEqual(response.status_code, 200)
if __name__ == '__main__':
unittest.main()
When I run these tests the assertTemplateUsed does not return a template and the response.status_code is 404. I imagine this is because self is not actually my application for some reason? When I run the app the root address definitely returns index.html.
Am I setting up create_app wrong? Any help is appreciated.
You need to create your Flask app instance in the setUp() function. At the moment the create_app() function doesn't get called at all.
Change your code like this:
import flask_testing
import unittest
from flask import Flask
from flask_testing import TestCase
class MyTest(TestCase):
def setUp(self):
self.app = Flask(__name__)
self.app_context = self.app.app_context()
self.app_context.push()
self.client = self.app.test_client(use_cookie=True)
def tearDown(self):
self.app_context.pop()
def test_greeting(self):
response = self.client.get('/')
print("should return 404 on landing page")
self.assertTemplateUsed('index.html')
self.assertEqual(response.status_code, 200)
if __name__ == '__main__':
unittest.main()
The setUp() function gets called prior to each test function. At first you will create a new instance of your Flask app. If you want to access items in your application context it is good practice to push it in your setUp() function and pop it in your tearDown() function. You can leave this out if you don' t access app_context items (like Database session objects) from your test function. At last you need to creat the test client in your setUp() function. You missed that part in your post but I guess you did it somewhere else in your code.
In your setUp function, you need to provide a test client to make the requests. Try something like this.
def setUp(self):
# this test client is what flask-testing will use to make your requests
self.app = app.test_client()
Check this out for more information How to Test a Flask Application.
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.
using the python flask module, i would like to have the
app = flask.Flask(__name__)
as a attribute of a class:
class Handler(object):
def __init__(self):
self.datastores = {}
self.websocket_queue = gevent.queue.JoinableQueue()
self.app = flask.Flask(__name__)
the problem is how to access decorators then?
#self.app.route('/socket.io/<path:remaining>')
def socketio(self, remaining):
That generates the error NameError: name 'self' is not defined
Thanks
You could try to use Flask-Classy as it provides an easy way to use classes with Python-Flask.
It depends - if you are adding handlers inside of a method of the Handler class it should work without issue:
def add_routes(self):
#self.app.route("/some/route")
def some_route():
return "At some route"
If you are attempting to add routes outside of Handler you will need to use a reference to your Handler instance:
handler = Handler()
#handler.app.route("/some/route")
def some_route():
return "At some route"