I am learning to use flask and flask_mongoengine to create a website. Follow the flask tutorial 1.0.2 version. But I ran into a problem, how to implement the get_db() and close_db() function?
Currently, what I am doing is :
myapp.py
....
def create_app():
app = Flask(__name__)
from db import db
db.init_app(app)
#app.route('/')
def home():
...
return app
db.py
from flask import g
from flask_mongoengine import MongoEngine
db = MongoEngine()
def get_db():
g.db = ???
return g.db
def close_db():
db = g.pop('db', None)
if db is not None:
??? # db.close() doesn't exist!!!
I am very confused about how to do this part. Can someone give any suggestions? In flask_mongoengine tutorial page, they don't implement the get_db() and close_db() ...
Confusion happens because in those tutorials there are too many programming patterns. In flask-1.0.2 tutorial they use getter method pattern and but flask-mongoengine relies on bootstraping a db to flask-app-instance, which relies on a builder pattern — Flask Application Factories. It may still be confusing but I'll show you how it's meant to be done.
Bootstrap a flask-mongoengine in create_app:
def create_app(test_config=None):
app = Flask(__name__)
# configure mongo settings here like in flask-mongoengine docs
g.db = db = MongoEngine()
db.init_app(app)
def get_db():
return g.db
def close_db():
pass
What's about close_db()? Well, that function exists in case db you've chosen needs some cleanup actions to be closed. But I haven't found in mongoengine docs any explicit mention that mongoengine db connection need a cleanup actions from you to be closed, so you can just pass it.
Related
I'm following a tutorial for creating a Flask app with Flask-SQLAlchemy. However, it has started raising an error when creating the database. How do I create the database?
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///project.db"
db.init_app(app)
from . import models
create_database(app)
return app
def create_database(app):
if not path.exists("website/project.db"):
db.create_all(app=app)
print("created database")
The line db.create_all(app=app) gives me this error:
SQLAlchemy.create_all() got an unexpected keyword argument 'app'
Flask-SQLAlchemy 3 no longer accepts an app argument to methods like create_all. Instead, it always requires an active Flask application context.
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///project.db"
db.init_app(app)
from . import models
with app.app_context():
db.create_all()
return app
There is no need for that create_database function. SQLAlchemy will already not overwrite an existing file, and the only time the database wouldn't be created is if it raised an error.
I am trying to define a mongodb object inside main flask app. And I want to send that object to one of the blueprints that I created. I may have to create more database objects in main app and import them in different blueprints. I tried to do it this way.
from flask import Flask, render_template
import pymongo
from admin_component.bp1 import bp_1
def init_db1():
try:
mongo = pymongo.MongoClient(
host='mongodb+srv://<username>:<passwrd>#cluster0.bslkwxdx.mongodb.net/?retryWrites=true&w=majority',
serverSelectionTimeoutMS = 1000
)
db1 = mongo.test_db1.test_collection1
mongo.server_info() #this is the line that triggers exception.
return db1
except:
print('Cannot connect to db!!')
app = Flask(__name__)
app.register_blueprint(bp_1, url_prefix='/admin') #only if we see /admin in url we gonna extend things in bp_1
with app.app_context():
db1 = init_db1()
#app.route('/')
def test():
return '<h1>This is a Test</h1>'
if __name__ == '__main__':
app.run(port=10001, debug=True)
And this is the blueprint and I tried to import the init_db1 using current_app.
from flask import Blueprint, render_template, Response, request, current_app
import pymongo
from bson.objectid import ObjectId
import json
bp_1 = Blueprint('bp1', __name__, static_folder='static', template_folder='templates')
print(current_app.config)
db = current_app.config['db1']
But it gives this error without specifying more details into deep.
raise RuntimeError(unbound_message) from None
RuntimeError: Working outside of application context.
This typically means that you attempted to use functionality that needed
the current application. To solve this, set up an application context
with app.app_context(). See the documentation for more information.
Can someone point out what am I doing wrong here??
The idea you are attempting is correct; however it just needs to be done a little differently.
First, start by declaring your mongo object in your application factory:
In your app/__init__.py:
import pymongo
from flask import Flask
mongo = pymongo.MongoClient(
host='mongodb+srv://<username>:<passwrd>#cluster0.bslkwxdx.mongodb.net/?retryWrites=true&w=majority',
serverSelectionTimeoutMS = 1000
)
# Mongo is declared outside of function
def create_app(app):
app = Flask(__name__)
return app
And then in your other blueprint, you would call:
from app import mongo # This right here will get you the mongo object
from flask import Blueprint
bp_1 = Blueprint('bp1', __name__, static_folder='static', template_folder='templates')
db = mongo
I've been following the flask tutorial to add a database, in their example they're using mysqlite while I'm using MySQL but I figured that shouldn't make a huge difference here. https://flask.palletsprojects.com/en/1.1.x/tutorial/database/
Here's the mysql library I'm trying to use https://flask-mysql.readthedocs.io/en/stable/
However whatever I do I can't seem to get away from this assertion error:
AssertionError: A setup function was called after the first request was handled. This usually indicates a bug in the application where a module was not imported and decorators or other functionality was called too late.
To fix this make sure to import all your view modules, database models and everything related at a central place before the application starts serving requests.
The error only happens when trying to use the /dbtest path.
Here's my minimal __init__.py
import os
from flask import Flask, render_template
from . import db
def create_app(test_config=None):
# create and configure the app
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
MYSQL_DATABASE_HOST='mysql-svc',
MYSQL_DATABASE_USER='root',
MYSQL_DATABASE_PASSWORD='root',
DEBUG=True,
TESTING=True
)
db.init_app(app)
# a simple page that says hello
#app.route('/hello')
def hello():
return 'Hello, World!'
#app.route('/dbtest')
def db_test():
with app.app_context():
cursor = db.get_cursor()
cursor.execute('SHOW DATABASES')
return cursor.fetchall()
return app
And here is db.py which is in the same directory
from flaskext.mysql import MySQL
from flask import current_app, g
def get_cursor():
if 'db_cursor' not in g:
mysql = MySQL()
mysql.init_app(current_app)
g.db_cursor = mysql.get_db().cursor()
return g.db_cursor
def close_db(e=None):
db = g.pop('db_cursor', None)
if db is not None:
db.close()
def init_app(app):
app.teardown_appcontext(close_db)
Everything I've read about this error seems to indicate that I'm somehow messing around with routing after I've tried to access the database or something? But I'm just following the tutorial for how the routing is set up. I'm probably missing something obvious here, I believe it's an issue with mysql.init_app now but I still can't figure out how to do this.
The issue may be related if DEBUG = True, just ran into this myself came here looking for a solution. The closed github ticket below has a comment indicating that it was an issue at some point. As of today it still appears to be an issue.
https://github.com/ga4gh/ga4gh-server/issues/791
In the end I had to say screw it to the whole g thing with getting the cursor, same goes for closing the db with a teardown. In the end it works with just this in my db.py:
from flaskext.mysql import MySQL
def init_db_connection(app):
mysql = MySQL()
mysql.init_app(app)
return mysql.get_db().cursor()
Which I call once from my __init__.py before I do any routing, this gets me the db cursor. Then I just freely use that variable within the routing.
I'm not sure how well this will scale up, and frankly I think the flask tutorials are rather poor as the mechanics behind g, app initialisation, routing, current_app and the rest are poorly explained. But what I've got works for now at least. I hope someone else can provide a better answer to this question.
Just encountered the same issue and found this interesting article here.
It's about to use plain SQLAlchemy and to avoid Flask-SQLAlchemy.
Instead of initializing the connection with
db=SQLAlchemy(app)
(which causes the issue in question) I followed the article and did it like this:
class Query:
def __init__(self):
SQLALCHEMY_DATABASE_URI = 'sqlite:///db/mydb.db'
engine = create_engine(SQLALCHEMY_DATABASE_URI, connect_args={'check_same_thread': False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
self.session = SessionLocal()
Base = automap_base()
Base.prepare(engine, reflect=True)
self.Node = Base.classes.mytable
def getTop20(self):
nodes = self.session.query(self.Node).order_by(desc(self.Node.date_modified)).limit(20)
return nodes
I have the following Flask app running on one machine:
app.py
from flask import Flask
from aves.extensions import db
def create_app(config_object=DevConfig):
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://user:password#localhost/dbname'
register_extensions(app)
return app
def register_extensions(app):
db.init_app(app)
query = "SELECT * FROM score LIMIT 50;"
result = db.engine.execute(query)
print(list(result))
return None
extensions.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
Now, I used pgAdmin's backup function to export my database, and import it again to a new machine. I try to run the same code, but now I'm getting the following error:
RuntimeError: application not registered on db instance and no application bound to current context
I had the database running on the new machine before, then I worked on the code n the other machine for a while, and now that I try it again on the new one it doesn't work (maybe its somehow still bound to the old database or something?).
When I try a simple script like the one below and run it, it works fine.
test_db_connection.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://user:password#localhost/dbname'
db = SQLAlchemy(app)
query = "SELECT * FROM score LIMIT 50;"
result = db.engine.execute(query)
print(list(result))
I got it to run by adding the following line to app.py: with app.app_context():
def create_app(config_object=DevConfig):
app = Flask(__name__)
with app.app_context():
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://user:password#localhost/dbname'
register_extensions(app)
return app
I have an app using Flask-Restful and I don't know how to get the database connection info from the application context.
Here is what I have so far:
app.py:
....
from flask.ext.restful import reqparse, abort, Api, Resource
app = Flask(__name__)
app.config['DATABASE'] = 'my.db'
api = Api(app)
api.add_resource(Foo, '/')
foo.py
...
from flask.ext.restful import Resource
class Foo(Resource):
def __init__(self):
self.db = get_db()
def post(self):
do_stuff_with_db()
I'd like the get_db method to look something like:
def get_db():
return database.connect(app.config['DATABASE'])
The problem is in order to get app.config into foo.py, I create a circular reference. Is there a clean, and hopefully idomatic way to get this?
I have seen this question, but the solution given doesn't actually resolve the circular import problem.
Well, for my project I use this kind of structure:
application/__init__.py
...
app = Flask(__name__)
...
db = SQLAlchemy(app) #because I use sqlalchemy
...
import application.core
application/core.py
from application.api import resource
...
# api definition
application/api/resource.py
from application import db
...
db.doWhatEverYouWant()
...
It's certainly not perfect but I don't have such kind of circular import problems.