Python: correct way to pass objects between modules - python

I am following the Flask SQLalchemy Quickstart which has all of the code in a single file:
Here is my initial index.py:
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)
# [snip] - some classes related to SQLAlchemy are here
if __name__ == '__main__':
app.run(host='0.0.0.0')
I want to split the code up a bit, so I created a separate file called database.py which will contain all of the database related code, and be imported as a module.
I modified my index.py to look like this:
from flask import Flask
# Import my database module
import database
app = Flask(__name__)
if __name__ == '__main__':
app.run(host='0.0.0.0')
And the file database.py:
from flask.ext.sqlalchemy import SQLAlchemy
app.config['SQLALCHEMY_DATABASE_URI]'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)
# [snip] - some classes related to SQLAlchemy are here
Obviously when I try to run this code I get the following error:
File "database.py", line 5, in <module>
app.config['SQLALCHEMY_DATABASE_URI]'] = 'sqlite:////tmp/test.db'
NameError: name 'app' is not defined
I can see that this is because the app object only exists within the parent module.
I could put the following lines back into index.py:
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)
But this creates a similar problem, whereby db is not available within the database.py file.
What is the correct way to code this?

You can import the object app into database.py by putting:
from index import app
in database.py.

Edited answer after comment:
Simply use
from index import app
in database.py.
Alternatively, with the
import index
statement, use index.app instead of app only.
This should help you get out of python's import hell.
Btw: Not sure which IDE you are using. I like pycharm a lot. Using it you can refactor code and issues such as above are prevented automagically.

Related

Flask-SQLAlchemy application context error

I am trying to use SQLAlchemy not in a view function (I was doing something like this with Flask-APSheduler).
I know that there were already a lot of topics related to this theme, but none of them were helpful to me.
So, first of all I will show my code:
./run.py
from app import create_app
from flask_config import DevConfig, ProdConfig
app = create_app(DevConfig)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)
./app/__init__.py
from flask import Flask
from .node import node
from .models import db
def create_app(app_config=None):
app = Flask(__name__, instance_relative_config=False)
app.config.from_object(app_config)
db.init_app(app)
app.register_blueprint(node)
return app
./app/models.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Users(BaseFuncs, db.Model):
...
./app/node.py
from flask import Blueprint, request
from .bot import bot, secret
import telebot
node = Blueprint('node', __name__)
#node.route('/{}'.format(secret), methods=['POST'])
def handler():
bot.process_new_updates([telebot.types.Update.de_json(request.get_data().decode('utf-8'))])
return 'ok', 200
./app/bot.py
from flask import current_app as app
...
#bot.message_handler(commands=['test'])
def cmd_test(message):
with app.app_context():
print(Users.query.filter_by(id=0).first())
So when I am trying to call cmd_test from my application I am getting this error:
RuntimeError: Working outside of application context.
This typically means that you attempted to use functionality that needed
to interface with the current application object in some way. To solve
this, set up an application context with app.app_context(). See the
documentation for more information.
I tried to use g variable and before_request methods, because every time before calling the database there is a call to the route 'handler', but this also doesn't work.
I also tried to use db.get_app(), but there was no effect.
So my question is how to call database right outside the views?

Flask- How to import db=SQLAlchemy in separate file

I want to add a new table or add data by calling db, but i got some problem when i try to import db
db return like this <SQLAlchemy engine=None>
which mean i didnt already doing this db.init_app(app)
this is my file struckture
Root
run.py
------>server/__init__.py
Config.py
import os
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY')
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:#localhost/flask_py'
MAIL_SERVER = 'smtp.googlemail.com'
MAIL_PORT = 587
MAIL_USE_TLS = True
MAIL_USERNAME = os.environ.get('EMAIL_USER')
MAIL_PASSWORD = os.environ.get('EMAIL_PASS')
Run.py
from server import server_app, db
app = server_app()
if __name__ == '__main__':
app.run(debug=True)
__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from server.config import Config
db = SQLAlchemy()
def server_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(Config)
db.init_app(app)
from server.users.routes import users
app.register_blueprint(users)
return app
in my command line using windows i want to import db, i try like this :
D:\PYTHON\root>python
from run import db
db.create_all()
but when i check is :
<SQLAlchemy engine=None>
I have a full working solution here very similar to what you're doing. Check my layout and then look at the create_db.py.
https://github.com/researcher2/stackoverflow_56885380
It appears in your case the "server_app" is not being executed in your interactive shell. Assuming you are running interactive shell in root directory, you would want to do the following:
from server import db, create_app
app = server_app()
with app.app_context():
db.create_all()
The annoying thing about flask-sqlalchemy as opposed to plain sqlalchemy is the db is coupled to a flask app. The db config comes from the flask config and the initiation is done during db_init or just SqlAlchemy(db) if you want the simpler version for single app setup.
Your models would also need to be setup properly. In my example above I just had them in the create_db script.
This post may help you as well regarding factories and blueprints. I created the above github to answer it.
Reflecting different databases in Flask factory setup

Using Flask-pymongo across multiple modules

I'm having some trouble understanding how to incorporate Flask-Pymongo. My app is initiated from my rrapp.py Inside of this file, I have
rrapp.py
#
# Imports up here
#
app = Flask(__name__)
mongo = PyMongo(app)
# Code down here
Now, to use this, I simply do mongo.db.users.find(). This works fine.
Now, say I have another file called userservice.py that I call methods from one of my endpoints within rrapp.py. How do I incorporate PyMongo(app) in my userservice.py file if I don't have access to the app object? Or am I missing something obvious here?
you should first define mongo oustside create_app to have access to it from inside other files.
then init_app with that like the following:
from flask import Flask, current_app
from flask_pymongo import PyMongo
mongo = PyMongo()
def create_app(config_name):
app = Flask(__name__, instance_relative_config=False)
app.config.from_object(app_config[config_name])
# INIT EXTENSIONS ----------------------
mongo.init_app(app)
return app
then in any file you can import mongo from above file. for example:
from ../factory import mongo

Flask nosetests - unable to connect to test db

I'm making a simple Flask web application for fun and I wanted to use nosetests. I'm stuck at how to use Flask-SQLAlchemy to connect to an in-memory test database in my tests file. When I run my tests - Flask connects to my main app's database and what is more, fails to clean it up after each test. Here's my tests code:
import nose
from nose.tools import *
from pyquery import PyQuery as pq
from flask.ext.sqlalchemy import SQLAlchemy
from app import site, db
from app.models import Post
class TestApp(object):
def setUp(self):
site.config['TESTING'] = True
site.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
self.test_app = site.test_client()
db.create_all()
def tearDown(self):
# db.session.remove()
db.drop_all()
def test_posts_index(self):
db.session.add(Post('title', 'body'))
db.session.add(Post('title2', 'body'))
db.session.commit() # this writes to production db ie app.db file
# instead of sqlite://
rv = self.test_app.get('/posts')
d = pq(rv.data)
print len(d('h1'))
assert len(d('h1')) == 2
And here's my app/__init__.py code:
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from app import config
site = Flask(__name__)
site.config['SQLALCHEMY_DATABASE_URI'] = config.db_uri
db = SQLAlchemy(site)
site.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
from app import db_setup
db_setup.create_db()
import controllers, models
The db_setup.create_db() in app/__init__.py function looks simply like this:
from app import db
from app.models import Post
def create_db():
db.create_all()
db.session.commit()
I tried instantiating the application and database in the tests file, but then my models don't work because they from app import db, where db is the production db object. I also sprinkled a few print statements in the test case like print db and they print out something like <SQLAlchemy engine sqlite://>, but it still writes to the production db anyways.
I'd really appreciate any tips on how to get around this. Thanks!
Why don't you use something about the environment to determine whether the app starts in a testing or live mode?
if 'testing' in os.environ:
site.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
else:
site.config['SQLALCHEMY_DATABASE_URI'] = config.db_uri
There are so many ways to skin this particular cat. If you don't like the idea of having if blocks littering your code you can import your settings from an entirely separate module based on whether the app is started in testing or live mode.
I was able to figure out the problem, it's related to me initiating a connection to the database in my __init__.py file, which I shouldn't do.
The culprit was the
from app import db_setup
db_setup.create_db()
code. Essentially, every time I did an from app import db, I think that app gets instantiated, it calls db_setup.create_db(), which creates the tables using the production config. From there on, despite trying to set the flask app config SQLALCHEMY_DATABASE_URI to an in memory database, the db object would continue to use the database instantiated in the __init__.py file.
All I have to do is call create_all() from the environment my will run in at that time. Hope this helps anyone how might run into something similar.
I had the same problem, but I didn't use a db.create_all() type statement in my init.py file at all.
In the end, the only way I could around the issue was to use
def setUp(self):
with app.app_context():
db.create_all()

Flask-SQLAlchemy import/context issue

I want to structure my Flask app something like:
./site.py
./apps/members/__init__.py
./apps/members/models.py
apps.members is a Flask Blueprint.
Now, in order to create the model classes I need to have a hold of the app, something like:
# apps.members.models
from flask import current_app
from flaskext.sqlalchemy import SQLAlchemy
db = SQLAlchemy(current_app)
class Member(db.Model):
# fields here
pass
But if I try and import that model into my Blueprint app, I get the dreaded RuntimeError: working outside of request context. How can I get a hold of my app correctly here? Relative imports might work but they're pretty ugly and have their own context issues, e.g:
from ...site import app
# ValueError: Attempted relative import beyond toplevel package
The flask_sqlalchemy module does not have to be initialized with the app right away - you can do this instead:
# apps.members.models
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Member(db.Model):
# fields here
pass
And then in your application setup you can call init_app:
# apps.application.py
from flask import Flask
from apps.members.models import db
app = Flask(__name__)
# later on
db.init_app(app)
This way you can avoid cyclical imports.
This pattern does not necessitate the you place all of your models in one file. Simply import the db variable into each of your model modules.
Example
# apps.shared.models
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
# apps.members.models
from apps.shared.models import db
class Member(db.Model):
# TODO: Implement this.
pass
# apps.reporting.members
from flask import render_template
from apps.members.models import Member
def report_on_members():
# TODO: Actually use arguments
members = Member.filter(1==1).all()
return render_template("report.html", members=members)
# apps.reporting.routes
from flask import Blueprint
from apps.reporting.members import report_on_members
reporting = Blueprint("reporting", __name__)
reporting.route("/member-report", methods=["GET","POST"])(report_on_members)
# apps.application
from flask import Flask
from apps.shared import db
from apps.reporting.routes import reporting
app = Flask(__name__)
db.init_app(app)
app.register_blueprint(reporting)
Note: this is a sketch of some of the power this gives you - there is obviously quite a bit more that you can do to make development even easier (using a create_app pattern, auto-registering blueprints in certain folders, etc.)
an original app.py: https://flask-sqlalchemy.palletsprojects.com/en/2.x/quickstart/
...
app = flask.Flask(__name__)
app.config['DEBUG'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = flask.ext.sqlalchemy.SQLAlchemy(app)
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
...
class Computer(db.Model):
id = db.Column(db.Integer, primary_key=True)
...
# Create the database tables.
db.create_all()
...
# start the flask loop
app.run()
I just splitted one app.py to app.py and model.py without using Blueprint. In that case, the above answer dosen't work. A line code is needed to work.
before:
db.init_app(app)
after:
db.app = app
db.init_app(app)
And, the following link is very useful.
http://piotr.banaszkiewicz.org/blog/2012/06/29/flask-sqlalchemy-init_app/

Categories

Resources