Flask-Restplus middleware - python

I have made an api with restplus
from flask_restplus import Namespace, Resource, fields
from flask import request
from flask_restplus import abort
api = Namespace('myproject', description='My API')
#api.route('/run')
class MyProject(Resource):
#api.doc(params={'name':'name'})
def post(self):
#doing stuff
return {'status':'started'}
The point of this route is to run another function and return {'status':'started'} immediatly without waiting for the function to end.
I've found this https://stackoverflow.com/a/51013358/1540114
but I have trouble using it in my project
from what I understood the AfterResponse is a middleware that I can apply to my route
I tried using it like this
....
api = Namespace('myproject', description='My API')
AfterResponse(api)
#api.after_response
def say_hi():
print("hi")
#api.route('/run')
class MyProject(Resource):
....
but it gave me an error stating that api doesn't have a wsgi_app property

Related

How to use flask app context when using decorators

I'd like to use token authorization from Flask-Security-Too for this "Article" endpoint. However, the decorator function #auth_token_required("token") needs the context of the app. The app is initialized in a different file.
I've added app.app_context() but I don't know how to tell the decorator to use it's context.
Note: I'm working with Flask-RestX for providing a SwaggerUI (and OpenAPI spec). Also, I use Blueprints/Namespaces to modularize the API - which makes it a bit more complex.
from flask import Blueprint
from flask_restx import Api, Resource, fields, Namespace
from flask_security import auth_token_required
from .db import db_session
from .models import Article
from flask import current_app as app
article_blueprint = Blueprint('api', __name__, url_prefix='/api/article')
flask_api = Api(article_blueprint, version='0.1', title='Article Manager',
description='Article management',)
article_manager = Namespace('article', description='Article Management Endpoint')
parser = article_manager.parser()
parser.add_argument('title', type=str, required=False,
help='Title of Article', location='json')
parser.add_argument('text', type=str, required=False,
help='Text of Article', location='json')
parser.add_argument('id', type=int, required=False,
help='ID of Article', location='json')
#article_manager.route('/<string:title>','/<int:id>')
class GetArticle(Resource):
# FIXME app.app_context() ?!
#auth_token_required("token")
def get(self, title=None, id=None):
# removed error handling for simplicity
if title is not None:
data = Article.query.filter(Article.title.ilike('%'+title+'%')).first()
if id is not None:
data = Article.query.filter_by(id=id).first()
db_session.commit()
return {'id': data.id, 'title': data.title, 'text': data.text}, 200
flask_api.add_namespace(article_manager)
Flask tells me:
Exception has occurred: RuntimeError
Working outside of application context.
The problem is in the way of using auth_token_required decorator. auth_token_required decorator unlike auth_required doesn't accept any additional arguments for configuration and expects the only decorated function to be transmitted.
Your code can be fixed by applying of one of the following variants:
#article_manager.route('/<string:title>', '/<int:id>')
class GetArticle(Resource):
#auth_token_required
def get(self, title=None, id=None):
pass
or
#article_manager.route('/<string:title>', '/<int:id>')
class GetArticle(Resource):
#auth_required("token")
def get(self, title=None, id=None):
pass
I am not familiar with flask_restx - however - an app_context is automatically created by flask when it first processes a request. You don't mention when you are getting this error - at config time or actual request time (or is it when you call add_namespace?). You might want to look at: https://flask.palletsprojects.com/en/2.0.x/appcontext/ to learn more about the appcontext.

Adding multiple endpoints into a Flask-RESTplus namespace from multiple files

I am working with Flask-Resplus API
I want to create multiple endpoints into a single namespace. That is simple, but if I want to split the code to add endpoints into multiple files, getting issues there.
Following is my app file:
from flask_restplus import Namespace, Api
from flask import Blueprint
from test_controller1 import test_ns
blueprint = Blueprint('api', __name__)
api = Api(blueprint,
title='Test API',
version='1.0',
description='Test API',
)
api.add_namespace(test_ns, path='/test')
test_controller1.py
#test_ns.route("/test1")
class Test(Resource):
def put(self):
pass
test_controller2.py
from test_controller1 import test_ns
#test_ns.route("/test2")
class Test(Resource):
def get(self):
pass
If I import test_ns from test_controller_1, only test1 endpoint will be added in the namespace.
How can I add both the endpoints(available in different files) in the same namespace?
This can be done by defining Namespace(with the same name) across the classes.
test_controller1.py
test_ns1 = Namespace("test", "Namespace for test")
test_controller2.py
test_ns2 = Namespace("test", "Namespace for test")
Add both the namespaces to the blueprint.

Can't get Pyramid to recognize Mongo

I am trying to get the Pyramid Web framework to handle a request using Mongo but I am a relative newbie to both. I cannot get my view to recognize a database attached to a request.
In development.ini:
###
# configure mongodb
###
mongo_uri = mongodb://localhost:27017/nomad
The __init__.py imports and main function:
# imports for Mongodb
from urllib.parse import urlparse
from gridfs import GridFS
from pymongo import MongoClient
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
config = Configurator(settings=settings)
init_includes(config)
init_routing(config)
db_url = urlparse(settings['mongo_uri'])
config.registry.db = MongoClient(
host=db_url.hostname,
port=db_url.port,
)
def add_db(request):
db = config.registry.db[db_url.path[1:]]
if db_url.username and db_url.password:
db.authenticate(db_url.username, db_url.password)
return db
def add_fs(request):
return GridFS(request.db)
config.add_request_method(add_db, 'db', reify=True)
config.add_request_method(add_fs, 'fs', reify=True)
config.scan()
return config.make_wsgi_app()
In jobscontroller.py, which is the handler view making the request:
import pyramid_handlers
from nomad.controllers.base_controller import BaseController
class JobsController(BaseController):
#pyramid_handlers.action(renderer='templates/jobs/index.pt')
def index(request):
all_jobs = request.db['jobs'].find()
return {'all_jobs': all_jobs}
I get an error:
all_jobs = request.db['jobs'].find()
AttributeError: 'JobsController' object has no attribute 'db'
I am using Pyramid handlers to manage routing and views, and I know that all of this works because all my routes resolve and deliver web pages. It's only the jobs controller that's funky, and only after I tried adding that request.db call.
Can someone help me understand what's going on?
You're not referring to the request - you're referring to the object itself (usually named self, but you have named it request - which would work if it was just a function and not a method on an object). Since you're inside an object of a class, the first parameter is always the object itself:
class JobsController(BaseController):
#pyramid_handlers.action(renderer='templates/jobs/index.pt')
def index(self, request):
all_jobs = request.db['jobs'].find()
return {'all_jobs': all_jobs}

Django Test mock instance of module variable

I'm trying to test my Django app which has a proxy API which is instantiated in its own module.
api.py
class ProxyApi(object):
def __init__(self, server_info):
pass
def validate_login(self, credentials):
# call to real api here
api = ProxyAPi()
middlewares.py
from mymodule.api import api
class MyMiddleware(MiddlewareMixin):
def process_request(self, request):
if api.validate_login():
# do something with proxy api
views.py
from mymodule.api import api
class TaskView(LoginRequiredMixin, FormView):
def get(self, request):
if api.validate_login():
# do something with proxy api
tests.py
class InputTasksViewTest(TestCase):
#mock.patch('mymodule.api.ProxyAPi')
def test_add(self, mock_api):
mock_api.validate_login.return_value = True
response = self.client.get(reverse('task'))
The original validate_loginis still called.
I would like to know how to handle the instantiation of ProxyApi while still retaining mocking capacity.
Ok I found my own solution, the problem was that once Django started, it read some files (like models or views or middlewares) that automatically instantiated api variable from import.
I just needed to defer this instantiation so I can mock the ProxyApi object, here's what I did:
api = SimpleLazyObject(lambda: ProxApi())
You have def validate_login(self, credentials): in api
and in middleware you define below code. So How you will send creadentials to API from middleware api.validate_login(<You should send credentials to api as parameter>):
from mymodule.api import api
class MyMiddleware(MiddlewareMixin):
def process_request(self, request):
if api.validate_login():
pass

Extending Flask class as main App

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.

Categories