Call decorators directly Flask, python - python

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.

Related

generating flask route from class method

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

Using aiohttp class based views without creating redundant endpoints

I am currently implementing an API using aiohttp. I am using class based views and I have a few endpoints with and without variable paths. When I try to combine the variable paths with the class based views I end up creating redundant endpoints. Below is some example code that describes my problem.
Let's say I want the following endpoints and methods:
GET api/users
DELETE api/users/{id}
I do his using the following code:
from aiohttp import web
class UserView(web.View):
async def get(self):
return web.json_response({"result": []})
async def delete(self):
user_id = self.request.match_info.get("id", None)
return web.json_response({"result": f"User {user_id} was deleted"})
if __name__ == "__main__":
app = web.Application()
app.router.add_view("/users", UserView)
app.router.add_view("/users/{id}", UserView)
web.run_app(app)
My code creates the following endpoints and method combinations.
GET api/users
GET api/users{id}
DELETE api/users
DELETE api/users/{id}
As you can see, I don't need all of them. I there any way I can still use class based views and variable paths without ending up with redundant endpoint/method combinations?
Apparently you can do this by passing the view to the router's get, post ... methods!
from aiohttp import web
class UserView(web.View):
async def get(self):
return web.json_response({"result": []})
async def delete(self):
user_id = self.request.match_info.get("id", None)
return web.json_response({"result": f"User {user_id} was deleted"})
if __name__ == "__main__":
app = web.Application()
app.router.get("/users", UserView)
app.router.delete("/users/{id}", UserView)
web.run_app(app)

How to use a custom error page in a class context?

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.

Flask-RESTful custom routes for each HTTP method

I have simple Resource class which defines some API methods:
class RoomAPI(Resource):
def get(self):
# some code
def post(self):
# some code
def put(self):
# some code
Then I define my routes like this:
api.add_resource(RoomAPI,'/api/rooms/')
So, my question is: how can I make different routes for each HTTP methos using only one Resource class?
I want to get such API:
GET /api/rooms/get/
POST /api/rooms/create/
PUT /api/rooms/update/
The short answer is, you shouldn't. That's not RESTful at all.
However, if you really want to, I think you could do it like so:
api.add_resource(RoomAPI,'/api/rooms/get', methods=['GET'])
api.add_resource(RoomAPI,'/api/rooms/create', methods=['PUT'])
api.add_resource(RoomAPI,'/api/rooms/update', methods=['POST'])
Since the unused **kwargs from add_resource get passed to add_url_rule().

Optional URL variables

Is there a way to define URLs with optional URL params in Flask? Essentially, what I'd like to do is define rules that allow for optionally specified languages:
/
/de -> matches / (but doesn't collide with /profile)
/profile
/de/profile
I think I've figured out a way to do it, but it involves either making a change to how Werkzeug and Flask handles the request (either monkey patching or forking the framework source). This seems like an overly complex way to deal with this problem though.. Is there an easier way to do this that I'm overlooking?
Edit:
Based on Brian's answer, here's what I came up with:
app.py:
from loc import l10n
def create_app(config):
app = Flask(__name__)
app.config.from_pyfile(config)
bp = l10n.Blueprint()
bp.add_url_rule('/', 'home', lambda lang_code: lang_code)
bp.add_url_rule('/profile', 'profile', lambda lang_code: 'profile: %s' %
lang_code)
bp.register_app(app)
return app
if __name__ == '__main__':
create_app('dev.cfg').run()
loc/l10ln.py
class Blueprint(Blueprint_):
def __init__(self):
Blueprint_.__init__(self, 'loc', __name__)
def register_app(self, app):
app.register_blueprint(self, url_defaults={'lang_code': 'en'})
app.register_blueprint(self, url_prefix='/<lang_code>')
self.app = app
(I haven't gotten to pulling lang_code from the variable list yet, but will be doing that shortly)
Now that's just hot imho.
Just in case you didn't know, you can register multiple routes for a view. Might be a pain to do it for every view, but it's doable...
DEFAULT_LANG = 'en'
#app.route('/profile')
#app.route('/<lang>/profile')
def profile(lang=DEFAULT_LANG):
pass
Or, perhaps you could implement your own route decorator that automatically invokes app.route for both scenarios...
from flask import Flask
app = Flask(__name__)
DEFAULT_LANG = 'en'
def lang_route(rule, **options):
def decorator(f):
endpoint = options.pop('endpoint', None)
app.add_url_rule(rule, endpoint, f, **options)
app.add_url_rule('/<lang>%s' % rule, endpoint, f, **options)
return f
return decorator
#lang_route('/profile') # also accepts '/<lang>/profile' automatically
def profile(lang=DEFAULT_LANG):
return lang
if __name__ == '__main__':
app.run(debug=True)
Blueprints might work for this, since they can be registered multiple times.
from flask import Flask, Blueprint
app = Flask(__name__)
bp = Blueprint('main', __name__)
#bp.route('/')
def hello(lang):
return 'Hello ' + lang + '!'
app.register_blueprint(bp, url_defaults={'lang': 'en'})
app.register_blueprint(bp, url_prefix='/<lang>')
if __name__ == '__main__':
app.run()
If that works, see Internationalized Blueprint URLs in the Flask documentation for a way to avoid specifying a lang argument in every view function.

Categories