Flask redirect from a different class - python

I'm trying to call a function that has a flask redirect which exists in a certain class.
My files are like so:
--- Lib/config.py
--- Lib/lib.py
--- auth.py
My code inside lib.py
import os
import json
import time
import requests
from Lib.config import APP_UID, REDIRECT_URI
from flask import redirect
class PyLi:
def __init__(self):
self.app_uid = APP_UID
self.redirect_uri = REDIRECT_URI
def code_redirect(self):
d = {'client_id=' + self.app_uid, 'redirect_uri=' + self.redirect_uri,
'response_type=' + 'code'}
return redirect("https://website.com/oauth/authorize?%s" %
("&".join(d)))
My config.py is just used to define the env variables I'm calling.
from decouple import config
APP_UID = config('APP_UID', cast=str, default=None)
...
And in auth.py I am calling it like so:
from Lib import lib
#token.route("/authorize", methods=['GET'])
def redirect():
auth = lib.PyLi()
auth.code_redirect()
But I'm getting TypeError: The view function did not return a valid response. The function either returned None or ended without a return statement.
What am I doing wrong?

You're missing a return on the last line of redirect() view function. Add it as follows:
#token.route("/authorize", methods=['GET'])
def redirect():
auth = lib.PyLi()
return auth.code_redirect()

Related

Why does url_for() gives routing.BuildError error in Flask html template [duplicate]

code:
blueprint:
from flask import Blueprint
from flask_restful import Api
################################
### Local Imports ###
################################
profile_api = Blueprint('profile_api', __name__)
api = Api(profile_api)
from .views import *
views:
class ShowProfPic(Resource):
def get(self):
return "hey"
api.add_resource(ShowProfPic, '/get profile picture/',endpoint="showpic")
how do we do a url_for with flask_restful?
because when I do.
url_for('showpic') it's a routing error
and when I do url_for('api.ShowProfPic')
it's also still a routing error
I've got the answer.
Apparently when working with blueprints
the way to access flask_restful's url_for is
url_for('blueprint_name.endpoint)
meaning an endpoint has to be specified on the resource
so using the example above:
profile_api = Blueprint('profile_api', __name__)
api = Api(profile_api)
from .views import *
class ShowProfPic(Resource):
def get(self):
return "hey"
api.add_resource(ShowProfPic, '/get profile picture/',endpoint="showpic")
to reference the ShowProfPic class and get it's endpoint
it's url_for('blueprint_name.endpoint')
so that's url_for(profile_api.showpic)

Python | Flask, using request.form in a class to POST data and update Jinja Template

I am building a small app using a forex converter api, it's function is to take one currency, and convert a value into the new currency. I seem to be getting caught when accessing my class "Survey" everything I try to get data from my html form. My program is getting caught on self.convertFrom=request.form['convertFrom'] and the python debugger is giving me "RuntimeError: Working outside of request context." I would greatly appreciate if someone can show/explain to me what it is im doing wrong here.
app.py
from flask_debugtoolbar import DebugToolbar
from forex_python.converter import CurrencyRates
from handleForm import Survey
app = Flask(__name__)
survey = Survey()
result=["Give me something to convert!"]
#app.route("/")
def home_page():
"""Loads home page where user can enter their first conversion"""
return render_template('index.html')
#app.route("/conversion")
def show_conversion():
"""shows the users conversion"""
return render_template('convSubmit.html', result=result)
#app.route("/conversion/new", methods=["POST"])
def add_conversion():
"""clear old conversion from list and add new"""
result=[]
result.append(survey.convertCurrency())
return redirect("/conversion")
handleForm.py
from flask import Flask, render_template, request
from forex_python.converter import CurrencyRates
c = CurrencyRates()
class Survey():
def __init__(self):
self.convertFrom=request.form['convertFrom'] <---gets caught here
self.convertTo=request.form['convertTo']
self.value=request.form['value']
def convertCurrency(self):
currencyFrom = self.convertFrom
currencyTo = self.convertTo
getValue = int(self.value)
result = c.convert(currencyFrom, currencyTo, getValue)
return result
The request variable will be available only when a request is active. In simple terms it will be available only when it is invoked by a view function handling a route.
In your case, you are trying to initialise the survey object outside any root function. That line will be invoked when the app server is started, before any request has been reserved, and hence flask is throwing an error saying that you are invoking it outside of request context.
To fix it, you should move the survey = Survey() inside a view function
#app.route("/conversion/new", methods=["POST"])
def add_conversion():
"""clear old conversion from list and add new"""
result=[]
survey = Survey()
result.append(survey.convertCurrency())
return redirect("/conversion")
While this would fix the problem, it is still not a good pattern to make that class constructor to directly access the request global.
If you need the constructor itself to initialize these params, you can pass these as arguments to the constructor and then pass them when initializing
from flask import Flask, render_template, request
from forex_python.converter import CurrencyRates
c = CurrencyRates()
class Survey():
def __init__(self, convertFrom, convertTo, value):
self.convertFrom=convertFrom <---gets caught here
self.convertTo=convertTo
self.value=value
def convertCurrency(self):
currencyFrom = self.convertFrom
currencyTo = self.convertTo
getValue = int(self.value)
result = c.convert(currencyFrom, currencyTo, getValue)
return result
And then change the view function to pass the values to the constructor
#app.route("/conversion/new", methods=["POST"])
def add_conversion():
"""clear old conversion from list and add new"""
result=[]
survey = Survey(request.form["convertFrom"], request.form["convertTo"], request.form["value"])
result.append(survey.convertCurrency())
return redirect("/conversion")

How works logging in Flask [duplicate]

I am trying to access access application configuration inside a blueprint authorisation.py which in a package api. I am initializing the blueprint in __init__.py which is used in authorisation.py.
__init__.py
from flask import Blueprint
api_blueprint = Blueprint("xxx.api", __name__, None)
from api import authorisation
authorisation.py
from flask import request, jsonify, current_app
from ..oauth_adapter import OauthAdapter
from api import api_blueprint as api
client_id = current_app.config.get('CLIENT_ID')
client_secret = current_app.config.get('CLIENT_SECRET')
scope = current_app.config.get('SCOPE')
callback = current_app.config.get('CALLBACK')
auth = OauthAdapter(client_id, client_secret, scope, callback)
#api.route('/authorisation_url')
def authorisation_url():
url = auth.get_authorisation_url()
return str(url)
I am getting RuntimeError: working outside of application context
I understand why that is but then what is the correct way of accessing those configuration settings?
----Update----
Temporarily, I have done this.
#api.route('/authorisation_url')
def authorisation_url():
client_id, client_secret, scope, callback = config_helper.get_config()
auth = OauthAdapter(client_id, client_secret, scope, callback)
url = auth.get_authorisation_url()
return str(url)
Use flask.current_app in place of app in the blueprint view.
from flask import current_app
#api.route("/info")
def get_account_num():
num = current_app.config["INFO"]
The current_app proxy is only available in the context of a request.
Overloading record method seems to be quite easy:
api_blueprint = Blueprint('xxx.api', __name__, None)
api_blueprint.config = {}
#api_blueprint.record
def record_params(setup_state):
app = setup_state.app
api_blueprint.config = dict([(key,value) for (key,value) in app.config.iteritems()])
To build on tbicr's answer, here's an example overriding the register method example:
from flask import Blueprint
auth = None
class RegisteringExampleBlueprint(Blueprint):
def register(self, app, options, first_registration=False):
global auth
config = app.config
client_id = config.get('CLIENT_ID')
client_secret = config.get('CLIENT_SECRET')
scope = config.get('SCOPE')
callback = config.get('CALLBACK')
auth = OauthAdapter(client_id, client_secret, scope, callback)
super(RegisteringExampleBlueprint,
self).register(app, options, first_registration)
the_blueprint = RegisteringExampleBlueprint('example', __name__)
And an example using the record decorator:
from flask import Blueprint
from api import api_blueprint as api
auth = None
# Note there's also a record_once decorator
#api.record
def record_auth(setup_state):
global auth
config = setup_state.app.config
client_id = config.get('CLIENT_ID')
client_secret = config.get('CLIENT_SECRET')
scope = config.get('SCOPE')
callback = config.get('CALLBACK')
auth = OauthAdapter(client_id, client_secret, scope, callback)
Blueprints have register method which called when you register blueprint. So you can override this method or use record decorator to describe logic which depends from app.
The current_app approach is fine but you must have some request context. If you don't have one (some pre-work like testing, e.g.) you'd better place
with app.test_request_context('/'):
before this current_app call.
You will have RuntimeError: working outside of application context , instead.
You either need to import the main app variable (or whatever you have called it) that is returned by Flask():
from someplace import app
app.config.get('CLIENT_ID')
Or do that from within a request:
#api.route('/authorisation_url')
def authorisation_url():
client_id = current_app.config.get('CLIENT_ID')
url = auth.get_authorisation_url()
return str(url)
You could also wrap the blueprint in a function and pass the app as an argument:
Blueprint:
def get_blueprint(app):
bp = Blueprint()
return bp
Main:
from . import my_blueprint
app.register_blueprint(my_blueprint.get_blueprint(app))
I know this is an old thread. But while writing a flask service, I used a method like this to do it. It's longer than the solutions above but it gives you the possibility to use customized class yourself. And frankly, I like to write services like this.
Step 1:
I added a struct in a different module file where we can make the class structs singleton. And I got this class structure from this thread already discussed. Creating a singleton in Python
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
else:
cls._instances[cls].__init__(*args, **kwargs)
return cls._instances[cls]
Step 2:
Then I created a Singleton EnvironmentService class from our Singleton class that we defined above, just for our purpose. Instead of recreating such classes, create them once and use them in other modules, routes, etc. import. We can access the class with the same reference.
from flask import Config
from src.core.metaclass.Singleton import Singleton
class EnvironmentService(metaclass=Singleton):
__env: Config = None
def initialize(self, env):
self.__env = env
return EnvironmentService()
def get_all(self):
return self.__env.copy()
def get_one(self, key):
return self.__env.get(key)
Step 3:
Now we include the service in the application in our project root directory. This process should be applied before the routes.
from flask import Flask
from src.services.EnvironmentService import EnvironmentService
app = Flask(__name__)
# Here is our service
env = EnvironmentService().initialize(app.config)
# Your routes...
Usage:
Yes, we can now access our service from other routes.
from src.services.EnvironmentService import EnvironmentService
key = EnvironmentService().get_one("YOUR_KEY")

Flask returns 404 in views

I am running unittests in a Flask app and I keep getting 404 when views.py file is not imported even though it is not used. I have such tests.py package:
import unittest
from presence_analyzer import main, utils
from presence_analyzer import views
class PresenceAnalyzerViewsTestCase(unittest.TestCase):
def setUp(self):
self.client = main.app.test_client()
def test_mainpage(self):
resp = self.client.get('/')
self.assertEqual(resp.status_code, 302)
When I delete views import the described problem occurs. Views are organized in a similar way to this:
from presence_analyzer.main import app
#app.route('/')
def mainpage():
return redirect('/static/presence_weekday.html')
And the main.py file:
import os.path
from flask import Flask
app = Flask(__name__) # pylint: disable=invalid-name
app.config.update(
DEBUG=True,
)
I guess it's something similar to what happened in this case, so I'm trying to change the application so that I don't have to make this dumb imports while testing. I've been trying to make use of the answer from above, but still can't make it work and these docs don't seem helpful. What am I doing wrong? main.py:
from flask.blueprints import Blueprint
PROJECT_NAME = 'presence_analyzer'
blue_print = Blueprint(PROJECT_NAME, __name__)
def create_app():
app_to_create = Flask(__name__) # pylint: disable=invalid-name
app_to_create.register_blueprint(blue_print)
return app_to_create
app = create_app()
views.py:
from presence_analyzer.main import app, blue_print
#blue_print.route('/')
def mainpage():
return redirect('/static/presence_weekday.html')
And tests.py has remained unchanged.
You must import views, or the route will not be registered. No, you are not executing the views directly, but importing executes code all module-level code. Executing code calls route. route registers the view function. You cannot get around needing to import a module in order to use the module.

I am restructuring my Flask-restful app, but having trouble placing the HTTP-auth in order to get app running

Essentially, I have a directory as such:
/app
runserver.py
/myapp
__init__.py
api.py
auth.py
/resources
__init.py
users.py
login.py
/models
__init.py
models.py
/common
/assets
In my auth.py I have a standard HTTP-basic username/password authentication. I will use these for areas where login is a must, and I want to verify each user. Login.py is where I need to add my decorator, but the whole app does not run due to this error: AttributeError: 'module' object has no attribute 'login_required'
from flask.ext.httpauth import HTTPBasicAuth
auth = HTTPBasicAuth()
#auth.verify_password
def verify_password(username, password):
user = User.query.filter_by(username = username).first()
if not user or not user.verify_password(password):
return False
g.user = user
return True
#auth.error_handler
def unauthorized():
return make_response(jsonify({'message': 'Unauthorized'}), 403)
My code for the login.py, which calls the decorator and then asks for the auth.
from flask_restful import Resource, reqparse
from myapp.models.users import User
from myapp import auth
class login(Resource):
decorators = [auth.login_required]
def __init__(self):
self.reqparse = reqparse.RequestParser()
self.reqparse.add_argument('userid', type = str , default="")
self.reqparse.add_argument('username', type = str, default="")
self.reqparse.add_argument('password', type = str, default="")
super(login, self).__init__()
def post(self):
args = self.reqparse.parse_args()
username = args['username']
password = args['password']
message = {'status': 'Authorized'}
return message
So to wrap it up, my question is: How and where do I add the flask-httpauth class so I can use the decorators. My option right now may be to paste that auth code in every resource class that needs it, but there seems there must be a better way to organize that. Help?
You are importing your auth module when really you want to be importing the HTTPBasicAuth object in that module. It is also possible you're running in to problems due to the fact that your module has the same name as the HTTPBasicAuth object.
I recommend renaming your auth.py to something else, such as authentication.py, and change your import to:
from ..authentication import auth
This gets a bit confusing because you have an auth.py module that defines an auth variable inside.
The line:
from myapp import auth
is importing the module, not the variable defined in it. Change it to:
from myapp.auth import auth
And I think that will work.
Sorry this is a bit old, but for the sake of others with this question, I would suggest not using flask.ext.httpauth. I found it isn't very useful. Here is how I do my HTTP basic auth with flask-restful.
This is in the myapp/init.py:
from flask import Flask, request
from flask.ext.restful import abort
def requires_auth(f):
#wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if not auth:
abort(401)
user = User.query.filter(User.username == auth.username).first()
auth_ok = False
if user != None:
auth_ok = verify_password(auth.password) == user.password
if not auth_ok:
return abort(401)
return f(*args, **kwargs)
return decorated
Resource script that has a resource that requires authorization to access the resource.
from myapp import requires_auth
#requires_auth
def get(self):
# do something

Categories

Resources