How to access app.config in a blueprint? - flask

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

Related

Separate routes in different files in Flask

I want to separate my /api endpoints from some others in another file.
Imagine I have this two files:
app.py ⤵
from flask import Flask
from routes.api_routes improt api
app = Flask(__name__)
app = app.register_blueprint(api)
app.route("/")
def index():
return "This is Index"
app.route("/about")
def about():
return "About page"
api_routes.py ⤵
from flask import Blueprint
api = Blueprint('api', __name__)
#api.route('/users')
def users():
return getUsers()
#api.route('/posts')
def posts():
return getPosts()
Let's say I want to access /api/users and /api/posts but I don't want to write the prefix "/api" before all of the endpoints that are on api_routes.py
How could I automatically add the prefix like you could do on Express.JS?
app.use("/api", apiRoute);
I thought on adding a PREFIX constant at the beggining of the file and appending the rest of the endpoint after it:
PREFIX = "/api"
...
#api.route(PREFIX+'/users')
What you can do is to set the Prefix when you are registering your blueprint like so:
app = app.register_blueprint(api, url_prefix='api')

Blueprint error multiple dashapp into flask

I'd like to host multiple dashapp into a flak server. Each dashapp shall be accessible with a login and password.
Some users can access different dashapps.
I tried the dash_auth.BasicAuth. It works perfectly but only for one dashapp.
So I tried to authenticate with flask_httpauth. Here again, it works well for one dashboard, but not for 2 and more because of blueprints.
My flask_app.py:
import dash
from flask import Flask, render_template, redirect, Blueprint
import dash_bootstrap_components as dbc
from flask_httpauth import HTTPDigestAuth
from apps.dashboard import Dashboard
app = Flask(__name__)
#app.route('/')
def hello_world():
return 'Hello from Flask!'
#others routes
auth = HTTPDigestAuth()
users = {
"john": "hello",
"susan": "bye"
}
#auth.get_password
def get_pw(username):
if username in users:
return users.get(username)
return None
url1 = '/dahsboard1'
dash_app1 = dash.Dash(__name__, server = app, external_stylesheets=[dbc.themes.BOOTSTRAP])
dash_app1.config.suppress_callback_exceptions = True
dash_app1.layout = Dashboard(dash_app1, 'data1', 'Title1', url1).layout
#app.route(url1)
#app.route(url1 + '/')
#app.route('/dash1')
#auth.login_required
def render_dashboard1():
return dash_app1.index()
url2 = '/dashboard2'
dash_app2 = dash.Dash(name='app2', server = app, external_stylesheets=[dbc.themes.BOOTSTRAP])
dash_app2.config.suppress_callback_exceptions = True
dash_app2.layout = Dashboard(dash_app2, 'data2', 'Title2', url2).layout
#app.route(url2)
#app.route(url2 + '/')
#app.route('/dash2')
#auth.login_required
def render_dashboard2():
return dash_app2.index()
if __name__ == '__main__':
app.run(debug=True)
The error:
ValueError: The name '_dash_assets' is already registered for a different blueprint. Use 'name=' to provide a unique name.
I undestand that a blueprint is created at each dashapp creation. After the first call :
print(app.blueprints)
returns
{'_dash_assets': <Blueprint '_dash_assets'>}
How can I add different blueprint names for each dashapp created ? Or more generally, how can I manage authentification for several dashapps running on one flask server ?
EDTIT:
I can solve this problem using this argument at dashboard creation
url_base_pathname = '/fake-url/'
But it leads to another problem: I can't protect this route with
#app.route('/fake-url/')
#auth.login_required(role=['admin'])
def render_dashboard():
return dash_app.app.index()
So the question is: how can I protect the route used in the dash creation with the argument url_base_pathname ?
You may have already solved this by now but will leave the solution here for the community. First you will need to set url_base_pathname for example:
dash_app2 = dash.Dash(
name='app2',
server = app,
url_base_pathname='/your_url_of_choice/'
external_stylesheets=[dbc.themes.BOOTSTRAP])
This will resolve that error.

flask-api-spec tags for grouping methods in swagger

I try to group my app-services in swagger. In my project, I use FlaskApiSpec to generate Swagger documentation for my Python-Flask application.
There is a part of code, how I generate swagger docs for my app, using Flask Blueprint pattern:
application/init.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_apispec.extension import FlaskApiSpec
from apispec.ext.marshmallow import MarshmallowPlugin
from apispec import APISpec
db = SQLAlchemy()
docs = FlaskApiSpec()
def create_app():
app = Flask(__name__)
app.config.update({
'APISPEC_SPEC': APISpec(
title = 'D&D Master Screen',
version = 'v0.1',
openapi_version = '2.0',
plugins = [MarshmallowPlugin()],
),
'APISPEC_SWAGGER_URL': '/api/swagger/',
'APISPEC_SWAGGER_UI_URL': '/api/swagger-ui/'
})
# Import Blueprints
from .characters.views import characters_bp
from .main.views import main_bp
# Registrate Blueprints
app.register_blueprint(characters_bp, url_prefix='/api/characters')
app.register_blueprint(main_bp, url_prefix='/')
# Initialize Plugins
db.init_app(app)
docs.init_app(app)
return app
application/characters/views.py
from flask import Blueprint
# from application.characters import characters_bp
from flask import jsonify
from flask_apispec import use_kwargs, marshal_with
from application import docs
from application.models import db, db_add_objects, db_delete_objects, Character
from application.schemas import CharacterSchema, ErrorSchema, CharacterIdSchema
characters_bp = Blueprint('characters_bp', __name__)
#characters_bp.route("/", methods=['GET'])
#marshal_with(CharacterSchema(many = True), code=200)
def characters():
characters = Character.get_all()
if characters is None:
return {"message": "Characters not found"}, 404
return characters
#characters_bp.route("/<character_id>", methods=['GET'])
#marshal_with(CharacterSchema, code=200)
#marshal_with(ErrorSchema, code=404)
def character(character_id):
character = Character.get(character_id)
if character is None:
return {"message": str("Character with id=" + character_id + " not found")}, 404
return character
#characters_bp.route("/", methods=['POST'])
#use_kwargs(CharacterSchema)
#marshal_with(CharacterIdSchema, code=200)
def create_character(**kwargs):
new_character = Character(**kwargs)
db_add_objects(new_character)
return {"id": new_character.id}
#characters_bp.route("/<character_id>", methods=['DELETE'])
#marshal_with(ErrorSchema, code=200)
#marshal_with(ErrorSchema, code=404)
def delete_character(character_id):
character = Character.get(character_id)
if character is None:
return {"message": str("Character with id=" + character_id + " not found")}, 404
db_delete_objects(character)
return jsonify({"message": "success"})
# Swagger docs for Characters Module services
blueprint_name = characters_bp.name
docs.register(character, blueprint = blueprint_name)
docs.register(characters, blueprint = blueprint_name)
docs.register(create_character, blueprint = blueprint_name)
docs.register(delete_character, blueprint = blueprint_name)
And result is
enter image description here
I want to group my /api/characters methods in one group in swagger, and name it correctly. I try to find a lot on the Internet, and learn something about tags is Swagger. But i don't understand, how to use this functionality in FlaskApiSpec
I suppose that tags can be added somewhere here:
docs.register(character, blueprint = blueprint_name)"
but don't understand how...
I also try for long time...
For me that works: just decorate your functions
#docs(tags=['characters'])
Yes, you can assign multiple methods under the same tag by using :
#docs(tags=['characters'])
Use same tag name for all the methods you want to show under the tag name. You can also add a description for every method separately in the tags.
Try this:
#doc(description='Describing GET method', tags=['charavters'])
def get():
<code here>
#doc(description='Describing POST method', tags=['charavters'])
def post():
<code here>

Zoho CRM Python SDK v2 initialization problem for Django

Im trying to integrate the Zoho CRM v2 SDK with my Django app.
On the Django runserver, im able to get access tokens and using the refresh method and store them in the zcrm_oauthtokens.pkl file. The sdk then automatically refreshes the access token using the refresh token, so no problem here. However on my production server (heroku) im getting this error message:
2019-01-16T11:07:22.314759+00:00 app[web.1]: 2019-01-16 11:07:22,314 - Client_Library_OAUTH - ERROR - Exception occured while fetching oauthtoken from db; Exception Message::'NoneType' object has no attribute 'accessToken'
It seems to me that the tokens are being saved to file, but when the sdk try to access them it is looking for them in a DB and not the file specified in the token_persistence_path.
In my settings.py I have this:
ZOHO_CLIENT_ID = config('ZOHO_CLIENT_ID')
ZOHO_CLIENT_SECRET = config('ZOHO_CLIENT_SECRET')
ZOHO_REDIRECT_URI = config('ZOHO_REDIRECT_URI')
ZOHO_CURRENT_USER_EMAIL = 'jamesalexander#mylastwill.co.uk'
ZOHO_PATH = os.path.join(BASE_DIR, 'wills_online', 'zoho')
zoho_config = {'apiBaseUrl': "https://www.zohoapis.com",
'currentUserEmail': ZOHO_CURRENT_USER_EMAIL,
'client_id': ZOHO_CLIENT_ID,
'client_secret': ZOHO_CLIENT_SECRET,
'redirect_uri': ZOHO_REDIRECT_URI,
'token_persistence_path': ZOHO_PATH}
and in a views file I have this:
from zcrmsdk import *
import logging
from django.shortcuts import HttpResponse
from wills.models import PersonalDetails, ZoHoRecord, WillDocument
from wills_online.decorators import start_new_thread
from wills_online.settings import zoho_config
logger = logging.getLogger(__name__)
class ZohoRunOnce:
def __init__(self):
self.already_run = False
def run_once(self):
if not self.already_run:
print('zoho init run once')
ZCRMRestClient.initialize(zoho_config)
self.already_run = True
zoho_init = ZohoRunOnce()
zoho_init.run_once()
print(zoho_config['token_persistence_path'])
def zoho_callback():
return HttpResponse(200)
#start_new_thread
def zoho_personal_details(request):
""" updates or create a user account on zoho on profile completion """
personal_details_ob = PersonalDetails.objects.get(user=request.user)
zoho_ob = ZoHoRecord.objects.get(user=request.user)
try:
if zoho_ob.account:
record = ZCRMRecord.get_instance('Accounts', zoho_ob.account)
record.set_field_value('Account_Name', request.user.email)
record.set_field_value('Name', personal_details_ob.full_name)
record.set_field_value('Email', request.user.email)
record.set_field_value('Address_Line_1', personal_details_ob.address_line_1)
record.set_field_value('Address_Line_2', personal_details_ob.address_line_2)
record.set_field_value('Post_Town', personal_details_ob.post_town)
record.set_field_value('Post_Code', personal_details_ob.post_code)
record.set_field_value('Dob_Day', personal_details_ob.dob_day)
record.set_field_value('Dob_Month', personal_details_ob.dob_month)
record.set_field_value('Dob_Year', personal_details_ob.dob_year)
record.set_field_value('Gender', personal_details_ob.sex)
record.set_field_value('Marital_Status', personal_details_ob.marital_status)
record.set_field_value('Partner_Name', personal_details_ob.partner_full_name)
record.set_field_value('Partner_Gender', personal_details_ob.partner_gender)
record.set_field_value('Partner_Email', personal_details_ob.partner_email)
record.set_field_value('Children', personal_details_ob.children)
record.set_field_value('Pets', personal_details_ob.pets)
record.update()
else:
user = ZCRMUser.get_instance(name='James Alexander')
record = ZCRMRecord.get_instance('Accounts')
record.set_field_value('Account_Owner', user)
record.set_field_value('Account_Name', request.user.email)
record.set_field_value('Name', personal_details_ob.full_name)
record.set_field_value('Email', request.user.email)
record.set_field_value('Address_Line_1', personal_details_ob.address_line_1)
record.set_field_value('Address_Line_2', personal_details_ob.address_line_2)
record.set_field_value('Post_Town', personal_details_ob.post_town)
record.set_field_value('Post_Code', personal_details_ob.post_code)
record.set_field_value('Dob_Day', personal_details_ob.dob_day)
record.set_field_value('Dob_Month', personal_details_ob.dob_month)
record.set_field_value('Dob_Year', personal_details_ob.dob_year)
record.set_field_value('Gender', personal_details_ob.sex)
record.set_field_value('Marital_Status', personal_details_ob.marital_status)
record.set_field_value('Partner_Name', personal_details_ob.partner_full_name)
record.set_field_value('Partner_Gender', personal_details_ob.partner_gender)
record.set_field_value('Partner_Email', personal_details_ob.partner_email)
record.set_field_value('Children', personal_details_ob.children)
record.set_field_value('Pets', personal_details_ob.pets)
response = record.create()
# save account id to db for future updates
zoho_ob.account = response.details['id']
zoho_ob.save()
except ZCRMException as ex:
logger.log(1, ex.status_code)
logger.log(1, ex.error_message)
logger.log(1, ex.error_details)
logger.log(1, ex.error_content)
print(ex.status_code)
print(ex.error_message)
print(ex.error_content)
print(ex.error_details)
Ive tried running ZCRMRestClient.initialize(zoho_config) in settings.py, with no luck.
My method for getting the access token and refresh token, which seems to work is:
import os
import pprint
from sys import argv
import django
import requests
import zcrmsdk
from django.conf import settings
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'wills_online.settings')
django.setup()
def zoho_refresh_token(code):
""" supply a self client token from the zoho api credentials from web site """
zoho_config = {"apiBaseUrl": "https://www.zohoapis.com",
"currentUserEmail": settings.ZOHO_CURRENT_USER_EMAIL,
"client_id": settings.ZOHO_CLIENT_ID,
"client_secret": settings.ZOHO_CLIENT_SECRET,
"redirect_uri": settings.ZOHO_REDIRECT_URI,
"token_persistence_path": settings.ZOHO_PATH}
pprint.pprint(zoho_config)
print('working')
address = f'https://accounts.zoho.com/oauth/v2/token?code={code}&redirect_uri={settings.ZOHO_REDIRECT_URI}&client_id={settings.ZOHO_CLIENT_ID}&client_secret={settings.ZOHO_CLIENT_SECRET}&grant_type=authorization_code'
response = requests.post(address)
data = response.json()
pprint.pprint(data)
zcrmsdk.ZCRMRestClient.initialize(zoho_config)
oauth_client = zcrmsdk.ZohoOAuth.get_client_instance()
refresh_token = data['refresh_token']
print(type(refresh_token))
oauth_client.generate_access_token_from_refresh_token(refresh_token, settings.ZOHO_CURRENT_USER_EMAIL)
print(refresh_token)
print('finished')
if name == 'main':
zoho_refresh_token(argv[1])
This is driving me mad. Help would be greatly appreciated. This is my first post so go easy, lol.
For future reference, you will need to define persistence_handler_class and persistence_handler_path in your configuration dictionary. You will also need a handler class and a user-defined model to store the results. Sample code follows:
# settings.py
import zcrmsdk
configuration_dictionary = {
'apiBaseUrl': 'https://www.zohoapis.com',
'apiVersion': 'v2',
'currentUserEmail': ZOHO_CURRENT_USER_EMAIL,
'sandbox': 'False',
'applicationLogFilePath': '',
'client_id': ZOHO_CLIENT_ID,
'client_secret': ZOHO_CLIENT_SECRET,
'redirect_uri': ZOHO_REDIRECT_URI,
'accounts_url': 'https://accounts.zoho.com',
'access_type': 'online',
'persistence_handler_class': ZOHO_HANDLER_CLASS,
'persistence_handler_path': ZOHO_HANDLER_PATH,
}
zcrmsdk.ZCRMRestClient.initialize(configuration_dictionary)
# zoho.models.py
from django.db import models
from zcrmsdk.OAuthClient import ZohoOAuthTokens
class ZohoOAuthHandler:
#staticmethod
def get_oauthtokens(email_address):
oauth_model_instance = ZohoOAuth.objects.get(user_email=email_address)
return ZohoOAuthTokens(oauth_model_instance.refresh_token,
oauth_model_instance.access_token,
oauth_model_instance.expiry_time,
user_email=oauth_model_instance.user_email)
#staticmethod
def save_oauthtokens(oauth_token):
defaults = {
'refresh_token': oauth_token.refreshToken,
'access_token': oauth_token.accessToken,
'expiry_time': oauth_token.expiryTime,
}
ZohoOAuth.objects.update_or_create(user_email=oauth_token.userEmail, defaults=defaults)
class ZohoOAuth(models.Model):
refresh_token = models.CharField(max_length=250)
access_token = models.CharField(max_length=250)
expiry_time = models.BigIntegerField()
user_email = models.EmailField()
In this example ZOHO_HANDLER_CLASS = 'ZohoOAuthHandler' and ZOHO_HANDLER_PATH = 'zoho.models'
The first time you go to use this you will need a grant_token from https://accounts.zoho.com/developerconsole. For the scope use aaaserver.profile.READ,ZohoCRM.modules.ALL to start (see https://www.zoho.com/crm/developer/docs/api/oauth-overview.html#scopes)
Before you can use the api you'll need to run the code below in a django shell. This uses a grant token to generate your initial access and refresh tokens. Afterwards, the api should handle refreshing your access token.
grant_token = GRANT_TOKEN
import zcrmsdk
oauth_client = zcrmsdk.ZohoOAuth.get_client_instance()
oauth_tokens = oauth_client.generate_access_token(grant_token)

How to mock redis for Django tests

I am trying to mock out redis in my Django application. I have tried several different methods but none seem to work. What am I doing wrong?
My primary redis instance is called with:
redis_client = redis.from_url(os.environ.get("REDIS_URL"))
That instance is imported in other parts of the app in order to add and retrieve data.
In my tests I tried doing:
import fakeredis
from mock import patch
class TestViews(TestCase):
def setUp(self):
redis_patcher = patch('redis.Redis', fakeredis.FakeRedis)
self.redis = redis_patcher.start()
self.redis.set('UPDATE', 'Spring')
print(redis_client.get('UPDATE'))
def tearDown(self):
self.redis_patcher.stop
When running the tests I want the 'UPDATE' variable to be set. But instead every instance of redis_client fails saying the server is not available. How can I mock out redis and set values, so that they are available when testing my app?
You should mock an item where it is used, not where it came from.
So if redis_client is used in a view like this:
myapp/views.py
from somemodule import redis_client
def some_view_that_uses_redis(request):
result = redis_client(...)
Then in your TestViews you should patch redis_client like this:
class TestViews(TestCase):
def setUp(self):
redis_patcher = patch('myapp.views.redis_client', fakeredis.FakeRedis)
self.redis = redis_patcher.start()