Python Flask Blueprint inject a variable value to all contexts globally? - python-3.x

I have the following app.py:
def create_app(env_name) -> Flask:
app = Flask(__name__)
app.register_blueprint(foo1, url_prefix="/foo1")
app.register_blueprint(foo2, url_prefix="/foo2")
app.register_blueprint(foo3, url_prefix="/foo3")
return app
and the following main.py:
from .app import create_app
app = create_app(env_name)
app.run(HOST, PORT)
and inside the individual blueprints / controllers / contexts, I defined a function which is needed by the template of the application:
foo1app = Blueprint("foo1", __name__)
#foo1app.context_processor
def inject_now():
return {'now': datetime.utcnow()}
Currently I copy-paste the code snippet above to ALL blueprints / controllers / contexts. Is there a way to inject the now globally without this code duplication? Thanks.

foo1app = Blueprint("foo1", __name__)
#foo1app.app_context_processor
def inject_now():
return {'now': datetime.utcnow()}
From the Blueprint class:
context_processor: Like Flask.context_processor but for a blueprint. This function is only executed for requests handled by a blueprint.
app_context_processor: Like Flask.context_processor but for a blueprint. Such a function is executed each request, even if outside of the blueprint.

Related

Call Method of another class in Flask API

I am trying to expose a data service as an API for a PHP application. I have written the API logic in a file called APILogic.py. The code in this looks like this
class APILogic(object):
def __init__(self):
# the initialization is done here
...
def APIfunction(self):
# the API logic is built here, it uses the class variables and methods
...
I have created another file for the API purpose. Its called API.py. The code in this file looks like this
import APILogic from APILogic
class MyFlask:
def __init__(self):
self.test = APILogic()
from flask import Flask
app = Flask(__name__)
my_flask = MyFlask()
#app.route("/Test")
def Test():
return my_flask.test.APIfunction
if __name__ == "__main__":
app.run(debug=True,port=9999)
When I run the code, I get the error
> TypeError: APIfunction() takes 1 positional argument but 3 were given
The view function did not return a valid response. The return type must be a string, dict, tuple, Response instance, or WSGI callable, but it was a method.
There are no arguments for the APIfunction though.
Please help.
The view function did not return a valid response. The return type must be a string, dict, tuple, Response instance, or WSGI callable, but it was a method.
Looks like you're returning the method, but it sounds like you want to return the result of that method:
#app.route("/Test")
def Test():
return my_flask.test.APIfunction()
View function should return valid response.
Sample API code
from flask import Flask
app = Flask(__name__)
#app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run()

How to make put request with nested dictionaries to flask-restful?

The documentation example for a simple restful api is:
from flask import Flask, request
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
todos = {}
class TodoSimple(Resource):
def get(self, todo_id):
return {todo_id: todos[todo_id]}
def put(self, todo_id):
todos[todo_id] = request.form['data']
return {todo_id: todos[todo_id]}
api.add_resource(TodoSimple, '/<string:todo_id>')
if __name__ == '__main__':
app.run(host="0.0.0.0",port="80",debug=True)
However, suppose I made a put request with a nested dictionary,ie {'data':{'fruit':'orange'}}. The TodoSimple would have request.form.to_dict() = {'data':'fruit'}. How can I work with the full nested dictionary?
You should probably use Schemas to achieve this goal. Take a good look at this first example of marshmallow docs:
https://marshmallow.readthedocs.io/en/3.0/
As flask-restful docs says:
The whole request parser part of Flask-RESTful is slated for removal
and will be replaced by documentation on how to integrate with other
packages that do the input/output stuff better (such as marshmallow).

AttributeError: module 'flask.app' has no attribute 'route'

I have autocomplete function as a separate file - autocomplete.py. The autocomplete is being imported into init.py as follows:
from xyzapp.autocomplete import autocomplete
The line that throws error in autocomplete.py is the following:
#app.route('/autocomplete',methods=['GET'])
The structure of the application is using blueprints and looks as follows:
appcontainer
|
|--run.py
|
|
|--xyzapp
|--__init__.py
|--autocomplete.py
|--blueprint_folder_1
|--blueprint_folder_2
|--static
The whole error message looks like this:
#app.route('/autocomplete',methods=['GET'])
AttributeError: module 'flask.app' has no attribute 'route'
Any ideas what I am doing wrong?
UPDATE:
The autocomplete.py looks as follows:
from flask import Flask, render_template, redirect, url_for, request, session, flash, app, Blueprint, jsonify
#app.route('/autocomplete',methods=['GET'])
def autocomplete():
database='backbone_test'
db=client[database]
all_names=list(db.ids.find({},{"current_name":1,"_id":0}))
return json.dumps(all_names)
The __init__.py file looks as follows:
from flask import Flask, render_template, Blueprint, jsonify, session
import jinja2
class MyApp(Flask):
def __init__(self):
Flask.__init__(self, __name__)
self.jinja_loader = jinja2.ChoiceLoader([self.jinja_loader,jinja2.PrefixLoader({}, delimiter = ".")])
def create_global_jinja_loader(self):
return self.jinja_loader
def register_blueprint(self, bp):
Flask.register_blueprint(self, bp)
self.jinja_loader.loaders[1].mapping[bp.name] = bp.jinja_loader
app = MyApp()
from xyzapp.autocomplete import autocomplete
from xyzapp.blueprint_folder_1.some_file import bp_1
app.register_blueprint(bp_1)
In autocomplete.py, it looks like you're importing app from Flask and not from your __init__.py file. Try importing from __init__.py instead.
import Flask first, the initiate your app
from flask import Flask
app = Flask(__name__)
I'm sorry this is a bit of a long shot, I'm not familiar with this way of running a Flask app, but it seems you create your own instance of the Flask class called MyApp and initialise it under the variable app.
I'm not 100% and may be completely wrong, but I think your problem lies in the __init__ of MyApp
Did you import Flask to your.py? Double check and make the addition of missing.

How do I properly set up a single SQLAlchemy session for each unit test?

When testing my Pyramid application using WebTest, I have not been able to create/use a separate Session in my tests without getting warnings about a scoped session already being present.
Here is the main() function of the Pyramid application, which is where the database is configured.
# __init__.py of Pyramid application
from pyramid_sqlalchemy import init_sqlalchemy
from sqlalchemy import create_engine
def main(global_config, **settings):
...
db_url = 'some-url'
engine = create_engine(db_url)
init_sqlalchemy(engine) # Warning thrown here.
Here is the test code.
# test.py (Functional tests)
import transaction
from unittest import TestCase
from pyramid.paster import get_appsettings
from pyramid_sqlalchemy import init_sqlalchemy, Session
from sqlalchemy import create_engine
from webtest import TestApp
from app import main
from app.models.users import User
class BaseTestCase(TestCase):
def base_set_up(self):
# Create app using WebTest
settings = get_appsettings('test.ini', name='main')
app = main({}, **settings)
self.test_app = TestApp(app)
# Create session for tests.
db_url = 'same-url-as-above'
engine = create_engine(db_url)
init_sqlalchemy(engine)
# Note: I've tried both using pyramid_sqlalchemy approach here and
# creating a "plain, old" SQLAlchemy session here using sessionmaker.
def base_tear_down(self):
Session.remove()
class MyTests(BaseTestCase):
def setUp(self):
self.base_set_up()
with transaction.manager:
self.user = User('user#email.com', 'John', 'Smith')
Session.add(self.user)
Session.flush()
Session.expunge_all()
...
def tearDown(self):
self.base_tear_down()
def test_1(self):
# This is a typical workflow on my tests.
response = self.test_app.patch_json('/users/{0}'.format(self.user.id), {'email': 'new.email#email.com')
self.assertEqual(response.status_code, 200)
user = Session.query(User).filter_by(id=self.user.id).first()
self.assertEqual(user.email, 'new.email#email.com')
...
def test_8(self):
...
Running the tests gives me 8 passed, 7 warnings, where every test except the first one gives the following warning:
From Pyramid application: __init__.py -> main -> init_sqlalchemy(engine):
sqlalchemy.exc.SAWarning: At least one scoped session is already present. configure() can not affect sessions that have already been created.
If this is of any use, I believe I am seeing the same issue here, except I am using pyramid_sqlalchemy rather than creating my own DBSession.
https://github.com/Pylons/webtest/issues/5
Answering my own question: I'm not sure if this is the best approach, but one that worked for me.
Instead of trying to create a separate session within my tests, I am instead using the pyramid_sqlalchemy Session factory, which is configured in the application. As far as I can tell, calls to Session in both test and application code return the same registered scoped_session.
My original intent with creating a separate session for my tests was to confirm that records were being written to the database, and not just updated in the active SQLAlchemy session. With this new approach, I've managed to avoid these "caching" issues by issuing Session.expire_all() at points in the tests where I transition between test transactions and application transactions.
# test.py (Functional tests)
import transaction
from unittest import TestCase
from pyramid.paster import get_appsettings
from pyramid_sqlalchemy import Session
from webtest import TestApp
from app import main
from app.models.users import User
class BaseTestCase(TestCase):
def base_set_up(self):
# Create app using WebTest
settings = get_appsettings('test.ini', name='main')
app = main({}, **settings)
self.test_app = TestApp(app)
# Don't set up an additional session in the tests. Instead import
# and use pyramid_sqlalchemy.Session, which is set up in the application.
def base_tear_down(self):
Session.remove()
class MyTests(BaseTestCase):
def setUp(self):
self.base_set_up()
with transaction.manager:
self.user = User('user#email.com', 'John', 'Smith')
Session.add(self.user)
Session.flush()
Session.expunge_all()
Session.expire_all() # "Reset" your session.
def tearDown(self):
self.base_tear_down()

Python: Request handler in Flask

I'm learning Flask, and the request handling seems to be like:
#app.route("/")
def hello():
return "Hello World!"
So I end up defining the functions for all my routes in a single file. I'd much rather have functions for a model in its own file, e.g. get_user, create_user in user.py. I've used Express (node.js) in the past, and I can do:
user = require('./models/user')
app.get('/user', user.list)
where user.coffee (or .js) has a list function.
How do I do the same in Flask?
From the docs:
A decorator that is used to register a view function for a given URL rule. This does the same thing as add_url_rule() but is intended for decorator usage
The add_url_rule docs elaborate:
#app.route('/')
def index():
pass
Is equivalent to the following:
def index():
pass
app.add_url_rule('/', 'index', index)
You can just as easily import your view functions into a urls.py file and call add_url_rule once for each view function there instead of defining the rules along side the functions or use the lazy loading pattern.

Resources