I have a setup where a node.js app is making ajax requests to a flask based python server. Since ajax requests lack cookie data, I can't use the simple flask session object to persist data across requests. To remedy this, I'd like to implement a redis based server side implementation of a session storage system, but the solutions I've found so far do not work.
One solution I've tried is the following this snippet.
But this doesn't work. Is there more setup I need to do to configure redis beyond what is mentioned in the quickstart guide? Here is my attempt:
...
from flask import session
# Snippet code is copy pasted here verbatum
import session_interface
...
app = Flask(__name__)
app.session_interface = session_interface.RedisSessionInterface()
...
# Can't access this as session['key'] across requests
session['key'] = value
...
if __name__ == '__main__':
app.secret_key = '123456789012345678901234'
app.run(debug=True)
Another solution I've tried is importing the Flask-Session extention.
However, I can't get this to work either. The section I'm confused about is the following:
"We are not supplying something like SESSION_REDIS_HOST and SESSION_REDIS_PORT, if you want to use the RedisSessionInterface, you should configure SESSION_REDIS to your own redis.Redis instance. This gives you more flexibility, like maybe you want to use the same redis.Redis instance for cache purpose too, then you do not need to keep two redis.Redis instance in the same process."
What is meant by this section and how would I have figured this out? Here is my attempt to make this extension work:
...
from flask import session
from flask_session import Session
import redis
...
app = Flask(__name__)
SESSION_TYPE = 'redis'
app.config.from_object(__name__)
Session(app)
...
# Can't access this as session['key'] across requests
session['key'] = value
...
if __name__ == '__main__':
app.secret_key = '123456789012345678901234'
app.run(debug=True)
Has anyone successfully implemented manual session storage on a server running flask? Are there other options for getting this functionality?
Thanks for your input.
I think that's because you missed the URL configuration for your storage Redis, to check that, you can use Redis-CLI to see if there is anything being inserted into Redis.
I use this code and it worked:
from flask import Flask
from flask_session import Session
import redis
……
app = Flask(__name__)
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_REDIS'] = redis.from_url('127.0.0.1:6379')
sess = Session()
sess.init_app(app)
def getSession():
return session.get('key', 'not set')
def setSession():
session.set('key')=123
return 'ok'
……
The following works for me:
...
from flask_session import Session
import redis
...
app = Flask(__name__)
SECRET_KEY = '123456789012345678901234'
SESSION_TYPE = 'redis'
SESSION_REDIS = redis.from_url('localhost:6379')
app.config.from_object(__name__)
sess = Session()
sess.init_app(app)
...
# Should be available accross requests now
session['key'] = value
...
if __name__ == '__main__':
app.run(debug=True)
Using sess.init_app(app) instead of Session(app) did the trick.
Related
I am working on a Flask application with a MongoDB database.
I am using that database for my core application data as well as for Flask-Dance token storage (https://flask-dance.readthedocs.io/en/latest/storages.html). The code for the custom storage backend is in token_storages.py, below.
Everything is working perfectly in the Flask development server, but when I try running my prod server via uWSGI I get this error:
UserWarning: MongoClient opened before f
ork. Create MongoClient only after forking. See PyMongo's documentation for details: https://pymongo.readthedocs.io/en/stable/faq.html#is-
pymongo-fork-safeUserWarning: MongoClient opened before f
ork. Create MongoClient only after forking. See PyMongo's documentation for details: https://pymongo.readthedocs.io/en/stable/faq.html#is-
pymongo-fork-safe
All the research I have seen refers to setting connect=False when instantiating PyMongo, but this is having no effect. Per the docs, this setting should be default regardless (https://flask-pymongo.readthedocs.io/en/latest/).
The error doesn't actually show me on what line things are going wrong -- any guidance on how to find what it PyMongo regards as errant would be much appreciated.
All the database calls that I can find are made within the Flask routes, which I believe should work fine (see the example from storyboard_routes.py below.
I believe the below code should be enough to provide details on the app construction but please let me know if anything else should be included.
Finally I did find that setting lazy-apps = true in my uWSGI settings does work around this issue (in wsgi.ini) but I would rather solve the root issue if possible.
Thanks in advance!
This was suggested, but at least today it is inaccurate as connect=true is not the current default. I tried it nevertheless without success.
MongoClient opened before fork. Create MongoClient only Flask
Related Versions
Python 3.8.10
pymongo 3.12.0
Flask-PyMongo 2.3.0
Flask 2.0.1
Flask-Dance 5.0.0
my_app/init.py
from flask import Flask
from flask_session import Session # https://pythonhosted.org/Flask-Session
from . import app_config
from .util import misc
from .routes import graph_auth_routes
from .routes import google_auth_routes
from .routes import storyboard_routes
from .db import init_db
def create_app():
app = Flask(__name__)
app.config.from_object(app_config)
azure_oauth = misc.create_azure_dance_blueprint(app)
google_oauth = misc.create_google_dance_blueprint(app)
app.config['MONGO_CONNECT'] = False # This has no effect since already the default
init_db(app)
Session(app)
app.register_blueprint(graph_auth_routes.bp)
app.register_blueprint(google_auth_routes.bp)
app.register_blueprint(storyboard_routes.bp)
return app
app=create_app()
my_app/db.py
from flask_pymongo import PyMongo
mongo = PyMongo()
def init_db(app):
print("Initializing DB")
mongo.init_app(app)
return app
my_app/util/token_storages.py
from flask import session
from flask_dance.consumer.storage import BaseStorage
from ..db import mongo
class MongoStorage(BaseStorage):
def __init__(self, oauth_provider):
self.oauth_provider = oauth_provider
def get(self, blueprint):
print("getting token")
try:
token = mongo.db["tokens"].find_one(
{
"contact_id": session["contact_id"],
"oauth_provider": self.oauth_provider,
}
)["oauth"]["token"]
return token
except:
print("can't find token for %s" % self.oauth_provider)
return None
def set(self, blueprint, token):
print("setting token")
query = mongo.db["tokens"].update_one(
{
"contact_id": session["contact_id"],
"oauth_provider": self.oauth_provider,
},
{"$set": {"oauth.token": token, "oauth_provider": self.oauth_provider}},
upsert=True,
)
def delete(self, blueprint):
print("deleting token")
mongo.db["tokens"].delete_one(
{
"contact_id": session["contact_id"],
"oauth_provider": self.oauth_provider,
}
)
return None
my_pp/routes/storyboard_routes.py
#bp.route("/page2_365")
#misc.default_error_handler
def page2_365():
# Update if Google integration was performed
if request.args.get("gc_integration"):
filter = {"_id": session["contact_id"]}
new_value = {
"$set": {"gc_integration": misc.str2bool(request.args["gc_integration"])}
}
mongo.db["contacts"].update_one(filter, new_value)
wsgi.ini
[uwsgi]
module = lead_wizard:app
master = true
processes = 5
workers=1
socket = /tmp/lead_wizard.sock
chmod-socket = 666
vacuum = true
die-on-term = true
I'm trying to neatly compartmentalize my code by separating flask app, service component and a decoupled key/value pair store.
My web app looks like this:
import flask
import os
import service_component as service
app = flask.Flask(__name__)
#app.route("/", methods=["POST"])
def event():
service.dostuff()
return "ok", 200
if __name__ == "__main__":
app.run(port=5000, debug=True)
# for test purposes
app.secret_key = os.urandom(24)
My service_component is:
import key_value_store
def dostuff():
val_i_want = key_value_store.provider["key"]
...and key_value_store looks like:
from flask import session
class SessionProvider:
def __getitem__(self, key):
return session.get(key)
def __setitem__(self, key, value):
session[key] = value
provider = SessionProvider()
The problem is that when I run the program, I get the error:
RuntimeError: The session is unavailable because no secret key was set. Set the secret_key on the application to something unique and secret.
Is there some requirement to keep the usage of "session" in my web app file? Are there recommended ways around this that still let me maintain loose coupling of components/abstraction of the key/value pair store?
Don't set the secret key here:
if __name__ == "__main__":
app.secret_key = os.urandom(24)
app.run(port=5000, debug=True)
return "ok", 200 # Also this does nothing here
The above will only apply when running with python app.py not with flask run or a WSGI server.
Also this would change the secret key on each launch, which you want to avoid. You should really be loading a pre-defined key in via your config, so user cookies aren't invalidated on each app restart. This sounds like it could lead to some weird session persistance behaviour.
If you don't have a config loader (yet), you can just hard-code the value right after you define the app:
app = Flask(__name__)
app.config['SECRET_KEY'] = 'super_secure'
Of course set that key to something sensible. If you want to generate this with Python, use the secrets module rather than os.urandom. However, again, you should passing the key as an actual string, rather than generating it dynamically on each app launch, for the reason mentioned above.
I want to set a basepath for my flask application. I have mentioned one example below.
basepath = 'http://localhost:3000/api'
i have two api call one is GET and other one is POST .
from flask import Flask
from flask_restful import Api
app = Flask(__name__)
api = Api(app)
api.add_resource(CreateUser, "/v1/user/create/")
api.add_resource(CreateUser, "/v1/user/details")
class CreateUser(Resource):
def post(self):
# Code for creating a user
def get(self):
# Code for get the details of user.
So here, if i want to create the user then my url will be http://localhost:3000/api/v1/user/create/
so same for GET also . So how do i achieve this ?
Initialize your Api with the path prefix:
from flask import Flask
from flask_restful import Api
app = Flask(__name__)
api = Api(app, "/api")
...
You can't change the host and port this way, you'll have to run flask with parameters:
flask run --host=127.0.0.1 --port=3000
Or you could do
from flask import Flask
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app, "/api")
...
if __name__ == "__main__":
app.run(host="127.0.0.1", port="3000")
Please keep in mind this is not intended for production environments, only for local testing. Please see https://flask.palletsprojects.com/en/1.1.x/tutorial/deploy/ for using in a production environment.
If you want to get those values from basepath, one option would be purl:
url = purl.URL('http://localhost:3000/api')
url.host() # --> "localhost"
url.port() # --> 3000
url.path() # --> "/api"
I'm writing a program to read mongodb document based on id field using flask-pymongo. But I'm getting error, can anybody tell me where am I going wrong?
code:
from flask import Flask, make_response, jsonify
from flask_pymongo import PyMongo
from collections import OrderedDict
from bson import json_util
import json
app = Flask('__name__')
app.config['MONGO_DBNAME'] = 'db_name'
app.config['MONGO_URI'] = 'mongodb://192.168.55.24:27017/db_name'
mongo_connection = PyMongo(app)
#app.route('/')
def index(inp_id):
collection = mongo_connection.db.table_name
one_record = collection.find_one({'id': inp_id})
obj_str = json_util.dumps(one_record)
obj_dict = json.loads(obj_str, object_hook=OrderedDict)
return make_response(jsonify(obj_dict), 200)
if __name__ == '__main__':
index('5cd00a468b36db516b6d2f16') # I think this is where I'm going wrong
giving me the below error:
RuntimeError: Working outside of application context.
If I pass id value directly in the place of inp_id I get the result but I'm trying to write a generic one.
Flask has an application context, You might need to use app.app_context() to make it work.
The application context keeps track of the application-level data
during a request, CLI command, or other activity. Rather than passing
the application around to each function, the current_app and g proxies
are accessed instead.
Try this :
def index(inp_id):
with app.app_context():
collection = mongo_connection.db.table_name
one_record = collection.find_one({'id': inp_id})
obj_str = json_util.dumps(one_record)
obj_dict = json.loads(obj_str, object_hook=OrderedDict)
return make_response(jsonify(obj_dict), 200)
For more information, read Flask Application context
I've found similar questions, but they seem to only cover mocking MongoDB and don't mention Flask.
I have a Flask app and I'm trying to unit test it with PyTest (including PyTest-Mongo and PyTest-Flask). However, before I can even get to the point of writing any tests, my test script crashes. The crash happens when importing the script with my Flash app: It's trying to create the PyMongo object without a url.
My question is: How can I ensure that PyMongo is mocked correctly at this point? According to the PyTest-Mongo documentation, the MongoDB test fixture should be passed to each of the test functions, but that doesn't help me if it's crashing on import.
test_app.py:
import pytest
import pytest_mongodb
from app import app
#pytest.fixture
def client():
app.config['TESTING'] = True
return client
app.py:
import ...
app = Flask(__name__)
app.config["MONGO_DBNAME"] = os.environ.get('DB_NAME')
app.config["MONGO_URI"] = os.environ.get('MONGO_URI')
app.secret_key = os.environ.get('SECRET')
mongo = PyMongo(app)
...
if __name__ == '__main__':
app.run(host=os.environ.get('IP'),
port=int(os.environ.get('PORT')),
debug=False)
we can wrap app and mongo in a function
This works because mongo is used as a local variable.
app.py
from flask import Flask
from flask_pymongo import PyMongo
def get_app_with_config(config):
app = Flask(__name__)
app.config.from_object(config)
mongo = PyMongo(app)
#app.route("/")
def index():
pass
.
.
return app, mongo
then we can create a test file and an application execution file with different databases:
test_app.py
from app import get_app_with_config
from config import TestConfig
app, mongo = get_app_with_config(TestConfig)
run.py
from app import get_app_with_config
from config import RunConfig
app, mongo = get_app_with_config(RunConfig)
if __name__ == '__main__':
app.run(port=8000)
Sample of config.py file:
class RunConfig:
MONGO_HOST = '192.168.1.37'
MONGO_PORT = 27017
MONGO_DBNAME = 'my_database'
MONGO_URI = f"mongodb://{MONGO_HOST}:{MONGO_PORT}/{MONGO_DBNAME}"
class TestConfig:
MONGO_HOST = '192.168.1.37'
MONGO_PORT = 27017
MONGO_DBNAME = 'my_database_test'
MONGO_URI = f"mongodb://{MONGO_HOST}:{MONGO_PORT}/{MONGO_DBNAME}"
TESTING = True
Needed a quick fix so I edited app.py so that it only hard-fails if PyMongo doesn't initialise when the file is executed (i.e. it ignores PyMongo's failed initialisation when running unit-tests.)
app = Flask(__name__)
app.config["MONGO_DBNAME"] = os.environ.get('DB_NAME')
app.config["MONGO_URI"] = os.environ.get('MONGO_URI')
app.secret_key = os.environ.get('SECRET')
try:
mongodb = PyMongo(app).db
except ValueError:
"""We don't provide a URI when running unit tests, so PyMongo will fail to initialize.
This is okay because we replace it with a version for testing anyway. """
print('PyMongo not initialized!')
mongodb = None
.
.
.
if __name__ == '__main__':
if not mongodb:
print('Cannot run. PyMongo failed to initialize. Double check environment variables.')
exit(1)
app.run(host=os.environ.get('IP'),
port=int(os.environ.get('PORT')),
debug=False)
In my tests file, I just assign the mocked mongoDB client to the app in the tests that need it. Definitely not the ideal solution.
def test_redacted(client, mongodb):
app.mongodb = mongodb
...