Can I avoid circular imports in Flask and SQLAlchemy - python

app/init.py:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__ name __)
db = SQLAlchemy(app)
from app import views, models
app/models.py:
from app import db # I want to avoid this everywhere
I really don't like my submodules having a dependency on their parent. Also can the global package variables be avoided too? I want a more OO solution.
One alternative for app is to use Blueprints I think, but then I loose the route decorator. Also the same cannot be done for db with SQLAlchemy (or can it?).

Take a look at this project: https://github.com/sloria/cookiecutter-flask
It's a great example for doing things the right way. Many of great Flask features are used: blueprints, application factories and more.
Here is how they register extensions, such as SQLAlchemy Database:
# app/extensions.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
...
# app/app.py
from app.extensions import db
def create_app(config_object=ProdConfig):
app = Flask(__name__.split('.')[0])
app.config.from_object(config_object)
register_extensions(app)
...
def register_extensions(app):
db.init_app(app)
...

Try use 3rd. We create exts.py file to instancing SQLAlchemy like this:
exts.py
from flask_sqlalchemy import SQLAlchemy
from flask_xxx import xxx
db = SQLAlchemy()
...
run.py
from flask import Flask
from .exts import db, ...
def register_extensions(app):
db.init_app(app)
...
def create_app(config):
app = Flask(__ name __)
app.config.from_object(config)
register_extensions(app)
return app
app = create_app(config)
models.py
from .exts import db
class XXXModel(db.Model):
pass

Related

Flask SQLALCHEMY_DATABASE_URI is not defined

I am having a problem setting up the skeleton of my Flask API, where I am getting an error regarding the URI for my database.
I have it set up like this
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from sqlite3 import Connection as SQLite3Connection
from datetime import datetime
from sqlalchemy import event
from sqlalchemy.engine import Engine
from flask import Flask, request, jsonify
app = Flask(__name__)
app.config[SQLALCHEMY_DATABASE_URI] = "sqlite:///sqlitedb.file"
app.config[SQLALCHEMY_TRACK_MODIFICATIONS] = 0
but I am getting the error in my IDE that [SQLALCHEMY_DATABASE_URI] is not defined and I also get the same error when trying to execute the program.
The modules import fine, so I'm not sure what I'm missing.
Edit: RESOLVED. I didn't have quotes around my config dictionary entries.
From the docs:
The config is actually a subclass of a dictionary and can be modified
just like any dictionary
this is how you modify app.config:
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///sqlitedb.file"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = 0
A crucial step is how SQLAlchemy gets informed of configuration. That'll look something like:
app = Flask(__name__)
# ... configuration
db = SQLAlchemy()
db.init_app(app)

How can I get a flask config attribute in a model that is not in the app context?

I put the SQLAlchemy object into the model.py:
db = SQLAlchemy()
And then import and use it in the app.py:
def create_app(config_filename):
app = Flask(__name__)
app.config.from_mapping(DEBUG=True)
from yourapplication.model import db
db.init_app(app)
However, if I need to add a session option, say autoflush, to the SQLAlchemy, and the option value is from the app object, say app.debug:
autoflush = app.debug
db = SQLAlchemy(session_options={'autoflush': autoflush})
How can I achieve this? I can't use the app.app_context or the g object because they aren't in app context, and I also don't want to create a new app instantiation here.

What should I do with SQLAlchemy NameError: Name 'db' is not defined

I am building a web app with Flask and SQLAlchemy. I can't seem to find out the reason for this error NameError: name'db' is not defined Would really appreciate your help.
from flask import Flask, render_template
from flask_bootstrap import Bootstrap
from flask_sqlalchemy import SQLAlchemy
db=SQLAlchemy()
app=Flask(__name__)
#create a function that creates a web application
# a web server will run this web application
def create_app():
app.debug=True
app.secret_key='BetterSecretNeeded123'
#set the app configuration data
app.config['SQLALCHEMY_DATABASE_URI']='sqlite:///bcib.sqlite'
#initialize db with flask app
db.init_app(app)
bootstrap = Bootstrap(app)
#importing modules here to avoid circular references, register blueprints of routes
from . import views
app.register_blueprint(views.bp)
#from . import admin
#app.register_blueprint(admin.bp)
return app
#app.errorhandler(404)
# inbuilt function which takes error as parameter
def not_found(e):
return render_template("404.html")
#app.errorhandler(500)
def internal_error(e):
return render_template("500.html")
When I try to input db.session.add(c1) in my terminal the error occurs
File "<stdin>", line 1, in <module>
NameError: name 'db' is not defined
Here the specific documentation : flask documentation
There is two ways to init the db :
you can binding the instance to a very specific Flask application like this
app = Flask(__name__)
db = SQLAlchemy(app)
you can create the object once and configure the application later to support :
db = SQLAlchemy()
def create_app():
*/
* Your code
/*
db = SQLAlchemy(app)
db.init_app(app) # HERE you need to call an init_app
return app
so in your case the best way is the second solution with the add of
db.init_app(app)
if you choose this solution, it will work normally.

Flask extension not registered in app.extensions

I want to access some extensions registered on my Flask app. I tried using app.extensions, but some of the extensions that I initialized aren't in the dict.
from flask import current_app
current_app.extensions.get(plugin_name)
extensions.py:
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_mail import Mail
from flask_debugtoolbar import DebugToolbarExtension
from flask_images import Images
from flask_redis import FlaskRedis
from flask_plugins import PluginManager
db = SQLAlchemy()
migrate = Migrate()
mail = Mail()
toolbar = DebugToolbarExtension()
images = Images()
redis = FlaskRedis()
plugin_manager = PluginManager()
main.py:
def configure_extensions(app):
db.init_app(app)
migrate.init_app(app, db)
mail.init_app(app)
toolbar.init_app(app)
images.init_app(app)
redis.init_app(app)
plugin_manager.init_app(
app,
plugin_folder="app/plugins",
plugin_import_path='app.plugins',
)
When I look at current_app.extensions, I only see some of the extensions. plugin_manager and toolbar are not in the list.
>>> for key in app.extensions: print(key)
migrate
mail
images
sqlalchemy
redis
Why aren't some extensions registered in app.extensions? Am I initializing them incorrectly? How can I get the extensions?
Extensions aren't required to add themselves to app.extensions, it's provided as an optional convenience. Apparently, those extensions don't add themselves. If you want that, consider submitting a patch to the relevant projects.
You don't need to use app.extensions to access the extension instances. Instead, import the instances you created, such as from myapp.extensions import db. That's the point of being able to create them separately from initializing them. When used in an app context (such as during a request), the objects will know what app to use.

How do I remove a circular dependency between a Flask blueprint and the application initialisation? [duplicate]

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