I have a module which I want to use for handling database access to both the production and a test database.
The content looks like this:
class FirstModel(db.Model):
#...
class SecondModel(db.Model):
#...
def get_production_connection():
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = PRODUCTION_DATABASE_URI
db = SQLAlchemy(app)
return db
def get_test_connection():
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = TEST_DATABASE_URI
db = SQLAlchemy(app)
return db
Unfortunately this is not working since the defined models inherit from db.Model which is not defined obviously when the classes are evaluated. Is there any way how I can make the classes/models to inherit from the db.Model class which is only accessible through the db object when one of the methods above is called?
I solved it now via:
db = SQLAlchemy()
#models here
def create_app():
app = Flask(__name__)
#db configuration
db.init_app(app)
return db
as shown here http://pythonhosted.org/Flask-SQLAlchemy/api.html#flask.ext.sqlalchemy.SQLAlchemy
Related
i have simple Flask app with flask security too and some unittest.
test.py:
class UserModelCase(unittest.TestCase):
def setUp(self):
self.app = create_app(TestConfig)
self.app_context = self.app.app_context()
self.app_context.push()
db.create_all()
def tearDown(self):
db.session.remove()
db.drop_all()
self.app_context.pop()
app/init.py
db = SQLAlchemy()
admin = Admin(name='abcd', template_mode='bootstrap4')
security = Security()
def create_app(config_class=Configuration):
app = Flask(__name__)
app.config.from_object(config_class)
db.init_app(app)
fsqla.FsModels.set_db_info(db)
from app.models import User, Role, Comment, Post
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
security.init_app(app, user_datastore, register_form=ExtendedRegisterForm)
...
return app
and some tests.
any test working, but if i run all tests on second i have an error:
sqlalchemy.exc.InvalidRequestError: Table 'roles_users' is already defined for this MetaData instance. Specify 'extend_existing=True' to redefine options and columns on an existing Table object.
at self.app = create_app(TestConfig)
but in my case i don't define table "roles_users", it's automatic created and i haven't definition for "roles_users" in my models.py
how can i run all tests avoid errors? help me pls
thx, jwag. thanks for the direction, after a little thought I added to testclass. and it helped
#classmethod
def setUpClass(cls) -> None:
cls.app = create_app(TestConfig)
The main issue here is that you need to create a NEW db instance for each test - since in create_app() you are doing db.init(app) - that is likely where things are failing.
This likely will apply to your Security and Admin object as well.
Try not using create_app() and doing the equivalent in your test fixture Setup()
I have a similar problem to the user here: SQLAlchemy Object already attached to session
And I'm getting basically the same error:
'<Link at 0x7f31a785f630>' is already attached to session '1' (this is '15')
I'm really trying to figure out why multiple sessions are being created, when I only want one. I have two files __init__.py and models.py:
Lines of interest from __init__.py:
from .models import User, draft_new_link_message, load_history, load_messages, db
# Initialize app and such
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///my.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
app.secret_key = 'super secret keyssss'
socketio = SocketIO(app)
db.init_app(app)
app.app_context().push()
...
db.create_all()
From models.py:
db = SQLAlchemy()
class Link(db.Model):
__tablename__ = 'link'
id = db.Column(db.Integer, primary_key=True, nullable=False)
url = db.Column(db.String(500), nullable=False)
originator_id = db.Column(db.Integer, db.ForeignKey('user.id'))
originator = db.relationship("User", back_populates='history')
From these lines alone, it seems that I should be on one session. If I'm not, how do I format my code correctly to reduce headaches and make sure I don't have to transfer objects between sessions? Thanks!
Edit: Solution
The reason I structured my project this way was because a few pieces of documentation said this was the correct pattern (creating the db inside your models file and then callng db.init_app() to get it into the main file). But I guess this was a bad idea. I thought maybe I had to because I can't have both the files reference each other. But to get around this I wrote a method in the main file to get the db and called the import on the models function
My new __init__.py:
# Initialize app and such
app = Flask(name)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///browse_together.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
app.secret_key = 'super secret keysssss'
socketio = SocketIO(app)
db = SQLAlchemy(app)
# Provide a way for models.py (and any other files that needs it) to get access to the database
def get_db():
return db
# Now you can import models.py because it can use this database
from . import urltils, models
from .models import User, Group, get_groups, create_group, \
draft_new_link_message, load_history, load_messages, toggle_send
The new first few lines from models.py:
from flask_login import UserMixin
from . import urltils
from . import get_db
# Get an instance of the db from __init__
db = get_db()
I think this is more correct.
The reason I structured my project this way was because a few pieces of documentation said this was the correct pattern (creating the db inside your models file and then callng db.init_app() to get it into the main file). But I guess this was a bad idea. I thought maybe I had to because I can't have both the files reference each other. But to get around this I wrote a method in the main file to get the db and called the import on the models function
My new __init__.py:
# Other imports...
# Initialize app and such
app = Flask(name)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///my.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
app.secret_key = 'super secret keysssss'
db = SQLAlchemy(app)
# Provide a way for models.py (and any other files that needs it) to get access to the database
def get_db():
return db
# Now you can import models.py because it can use this database
from . import urltils, models
from .models import User, Group, get_groups, create_group, \
draft_new_link_message, load_history, load_messages, toggle_send
The new first few lines from models.py:
from flask_login import UserMixin
from . import urltils
from . import get_db
# Get an instance of the db from __init__
db = get_db()
I think this is more correct.
I'm currently trying to integrate GraphQL (using graphene and flask_graphql) into my flask app. Tried a few tutorial from here and here. But seems none works in my situation.
Currently my project is like this
-manage.py
|app
|__init__.py (create_app is here)
|mod_graphql (this is folder)
|__init__.py (blueprint created here)
|models.py
|schema.py
|controller.py
The issue is in both tutorial, it recommend to create Base and engine in the models file, I did the same, but I would need to read config file using current_app for the URI to create engine. But actually, because it happens in flask initialization, so there is no request context yet, so current_app doesn't exist. so everything fails.
Would it be possible to help me setup this?
Below are some code:
app/__init__.py
create_app():
...
from .mod_graphql import bp_graph
app.register_blueprint(bp_graph)
...
mod_graphql/__init__.py
from flask import Blueprint
bp_graph = Blueprint('graphql', __name__)
from . import controller
from . import models
from . import schema
mod_graphql/models.py
Base = declarative_base()
engine = create_engine(current_app.config.get('BASE_URI'),
convert_unicode=True)
db_session = scoped_session(sessionmaker(autocommit=False,
autoflush=False,
bind=engine))
Base.query = db_session.query_property()
class Model1_Model(Base):
...
class Model2_Model(Base):
...
mod_graphql/schema.py
class Model1(SQLAlchemyObjectType):
class Meta:
model = Model1_Model
interfaces = (relay.Node,)
class Model2(SQLAlchemyObjectType):
class Meta:
model = Model2_Model
interfaces = (relay.Node,)
class Query(graphene.ObjectType):
node = relay.Node.Field()
schema = graphene.Schema(query=Query, types=[Model1])
mod_graphql/controller.py
bp_graph.add_url_rule('/graphql',
view_func=GraphQLView.as_view('graphql',
schema=schema,
graphiql=True,
context={'session':
db_session}))
#bp_graph.teardown_app_request()
def shutdown_session(exception=True):
db_session.remove()
When I try to start server, it tells me:
Working outside of application context
Would you pls recommend the best practice to setup this?
Thanks a lot!
I would suggest using the flask initialization to define the DB connection. This way you can use Flask's internal implementation of connecting to the database without defining it on your own
app/__init__.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(config_class)
db.init_app(app)
Then all you need to do in your models is:
app/models.py
Base = declarative_base()
class Model1_Model(Base):
Then in your route definition instead of using the db_seesion from your model inside your context, you can reference Flask's db like context={'session': db.session}) that you created in the create_app() function.
Try initiate the Blueprint in
app.__init__
I got a same time like u,cos the
__name__
should be app
Finally figured out how to do it without using current_app:
in config.py, and multi database binding by:
SQLALCHEMY_BINDS = {
'db1': DB1_URI,
'db2': DB2_URI
}
in models.py, do this
engine = db.get_engine(bind='db1')
metaData = MetaData()
metaData.reflect(engine)
Base = automap_base(metadata=MetaData)
class Model1(Base):
__tablename__ = 'db1_tablename'
In this way, the model will be able to access the proper database uri without current_app, but if app_context switches, potentially different solution will be needed
Checking Flask and Flask-SQLAlchemy doc i have a confusion, if i have:
models.py:
from flask_sqlalchemy import SQLAlchemy
#:Use or not
db = SQLAlchemy()
class User(db.Model):
__tablename__ = "USERS"
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(25), nullable=False)
password = db.Column(db.String(255), nullable=False)
config.py:
import os
class Config(object):
DEBUG = True
SECRET_KEY = os.urandom(12)
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_DATABASE_URI = "mysql://root:#localhost/example"
app.py:
from config import Config
from models import db
from models import User
app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)
if __name__ == "__main__":
db.init_app(app)
with app.app_context():
db.create_all()
app.run()
is necessary use:
db = SQLAlchemy(app) after app.config.from_object(Config)
or db.init_app(app) is the same?
Because i founded some examples of flask with only db = SQLAlchemy() in models.py and have db.init_app(app) before app.run()
And so founded examples with db = SQLAlchemy(app) override in app.py with no db.init_app(app)
I printed both values and get:
with only db in models:
<SQLAlchemy engine=None>
the problem is:
The app create the tables in my database
But engine=None
with db = SQLAlchemy(app) override
<SQLAlchemy engine=mysql://root:***#localhost/hsl_db?charset=utf8>
the problem is:
The app dont create the tables in my database
What is the correct way to assign the database of SQLAlchemy to Flask app?
From: https://flask-sqlalchemy.palletsprojects.com/en/2.x/api/
There are two usage modes which work very similarly. One is binding the instance to a very specific Flask application:
app = Flask(__name__)
db = SQLAlchemy(app)
The second possibility is to create the object once and configure the application later to support it:
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
db.init_app(app)
return app
The difference between the two is that in the first case methods like create_all() and drop_all() will work all the time but in the second case a flask.Flask.app_context() has to exist.
There is no correct way as it all depends on how you want to instantiate your db using SQLAlchemy and app using Flask.
But I'll go over how I use the app.
def create_app():
app = Flask(__name__, static_folder='static', instance_relative_config=True)
app.config.from_pyfile('app_config.py')
return app
app = create_app()
db = SQLAlchemy(app)
The Flask documentation recommends:
db.init_app(app)
Flask Factories & Extensions
I have a flask app that looks like this:
myapp
-- application.py
-- models.py
-- queries.py
-- routes.py
-- settings.py
application.py looks like this:
from flask import Flask
from myapp import routes
def create_app(config_object):
app = Flask(__name__)
app.config.from_object(config_object)
app.register_blueprint(routes.main)
return app
queries.py looks like this
from myapp.models import User
class QueryConnection(DATABASE_URI):
self.engine = create_engine(DATABASE_URI)
self.session = sessionmaker(bind=self.engine)
self.Session = self.session()
def get_all_users(self):
return self.Session.query(User).all()
routes.py looks like this:
from flask import current_app, Blueprint, render_template
from myapp import queries
main = Blueprint('main', __name__, url_prefix='/')
query_connection = queries.QueryConnection(current_app.config['DATABASE_URI'])
#main.route("/")
def index():
return render_template('index.jinja2', list=query_connection.get_all_users())
models.py looks like this:
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
I want to get the current_app.config['DATABASE_URI'] in routes.py so I can query the database but I don't know how to pass the app context from application.py to queries.py. I tried using this in application.py
with app.app_context():
app.register_blueprint(main)
But I get the error "working outside of application context". Anything else gives the same error.
I want to encapsulate the queries in its own class so that I can pass the database uri in different contexts like in tests.
you need to import the file, from where you are calling the create_app method. My guess is that it is the application.py.
--- D_file.py
app = create_app(your_config)
--- queries.py
from D_file import app
DB_URI = app.config['DATABASE_URI']
Why are you involving the Flask application at all in the SQLAlchemy setup? You have a configuration object available to you, use that directly. Then you can attach a session to the flask global object, or create a session dynamically.
In queries.py
import get_config from config
config_object = get_config()
engine = create_engine(config_object['DATABASE_URI'])
session_factory = sessionmaker(bind=engine)
Session = scoped_session(session_factory)
def get_all_users():
session = Session()
users = session.query(User).all()
session.close()
return users
And in your app factory:
from flask import Flask, g
from myapp import routes
from myapp.queries import Session
def create_app(config_object):
app = Flask(__name__)
app.config.from_object(config_object)
app.register_blueprint(routes.main)
# automatically set up and close a session for each request
#app.before_request
def before_request(response):
g.session = Session()
#app.after_request
def after_request(response):
g.session.close()
return app