I am developing a Flask application. It is still relatively small. I had only one app.py file, but because I needed to do database migrations, I divided it into 3 using this guide:
https://realpython.com/blog/python/flask-by-example-part-2-postgres-sqlalchemy-and-alembic/
However, I now can't run my application as there is a circular dependency between app and models.
app.py:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask import render_template, request, redirect, url_for
import os
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ['DB_URL']
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.debug = True
db = SQLAlchemy(app)
from models import User
... routes ...
if __name__ == "__main__":
app.run()
models.py:
from app import db
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
email = db.Column(db.String(120), unique=True)
def __init__(self, username, email):
self.username = username
self.email = email
def __repr__(self):
return self.username
manage.py:
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
from app import app, db
migrate = Migrate(app, db)
manager = Manager(app)
manager.add_command('db', MigrateCommand)
if __name__ == "__main__":
manager.run()
They are all in the same directory. When I try to run python app.py to start the server, I receive an error which definitely shows a circular dependency (which is pretty obvious). Did I make any mistakes when following the guide or is the guide wrong? How can I refactor this to be correct?
Thanks a lot.
EDIT: Traceback
Traceback (most recent call last):
File "app.py", line 14, in <module>
from models import User
File "/../models.py", line 1, in <module>
from app import db
File "/../app.py", line 14, in <module>
from models import User
ImportError: cannot import name User
I propose the following structure:
# 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)
...
# manage.py
from yourapp.app import create_app
app = create_app()
app.debug = True
...
In this case, database, app, and your models are all in separate modules and there are no conflicting or circular imports.
I chased this for a few hours, landing here a few times, and it turned out I was importing my page modules (the ones holding the #app.route commands) before the line where the app was created. This is easy to do since import commands tend to be placed at the very beginning, but it doesn't work in this case.
So this:
# app/__init__.py
print("starting __init__.py")
from flask import Flask
from flask import render_template
import matplotlib.pyplot as plt
import numpy as np
import mpld3
app = Flask(__name__, instance_relative_config=True)
app.config.from_object('config')
from . import index
from . import simple
app.run(threaded=False)
print("finished __init__.py")
Instead of having all imports on top.
Placing this here because this has to be a common error for casual flask users to encounter and they are likely to land here. I have hit it as least twice in the last couple of years.
Related
This is my flask project directory structure,
src/models-> UserModel.py
-> PassengerModel.py
src/run.py
and this run.py file contains the database connection object,
from flask import Flask
from flask_restful import Api
from flask_sqlalchemy import SQLAlchemy
from flask_cors import CORS
from flask_jwt_extended import JWTManager
from flask_restful import Resource
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///appDB.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SECRET_KEY'] = 'some-secret-string'
app.config['JWT_SECRET_KEY'] = 'jwt-secret-string'
app.config['JWT_BLACKLIST_ENABLED'] = True
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = ['access', 'refresh']
app.config['JWT_ACCESS_TOKEN_EXPIRES'] = False
CORS(app)
db = SQLAlchemy(app)
jwt = JWTManager(app)
db.create_all()
#app.before_first_request
def create_tables():
db.create_all()
api = Api(app)
from src.models import UserModel, PassengerModel, DriverModel, OwnerModel,
VehicleModel, TripPlanModel, TripStatusModel,PickupLocationsModel,
WaypointsModel, DriverFeedbackModel, PassengerFeedbackModel
once the all these models inside one file, db.create_all() method works fine and creates the database file.
But when all the model classes inside the models directory and even after imports it to run.py file db.create_all() method is not working.
How can I create DB from models in a different directory?
Thanks in advance.
try this:
File structure:
src/models -> UserModel.py
-> PassengerModel.py
-> __init__.py
/__init__.py
src/run.py
Import models:
try:
from src.models.usermodel import User
from src.models.passengermodel import Passenger
print('Models imported')
except ImportError as e:
print(e)
Hope that helps you. Cheers
PS: Probably you will need to put the imports for the models at the bottom of your file before the following:
Imports ...
...
your code
...
*** models imports here ***
if __name__ == '__main__':
app.run()
I am writing a flask application. I have two files. main.py and databases.py. I want to create a database from the database.py file. The main.py should access the databases.py file and create the database and table named "Users". But it shows import error. Help me with this issue
main.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from databases import User
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/data_log.db'
db = SQLAlchemy(app)
if __name__ == '__main__':
db.create_all()
app.run(host='0.0.0.0', port=5001)
databases.py
from main import db
from passlib.apps import custom_app_context as pwd_context
class User(db.Model) :
__tablename__ = 'users'
user_id = db.Column(db.Integer, primary_key = True)
username = db.Column(db.String(32), index = True)
password = db.Column(db.String(128))
def hash_password(self, password) :
self.password =pwd_context.hash(password)
def verify_password(self, password) :
return pwd_context.verify(password, self.password)
Traceback:
Traceback (most recent call last):
File "main.py", line 3, in <module>
from databases import User
File "/home/paulsteven/stack/databases.py", line 1, in <module>
from main import db
File "/home/paulsteven/stack/main.py", line 3, in <module>
from databases import User
ImportError: cannot import name 'User'
This is a regular case of cyclic imports conflict.
The traceback gives you a clear steps:
How it goes in steps:
you run main.py
the control flow starts importing features/libraries till it gets the 3rd line
from databases import User.
it goes to the databases module to find the needed User class. But ... the User may use the outer scope features (and it does require db.Model), so the control flow needs to scan databases module from the start. This reflects the 2nd step from traceback ("/home/paulsteven/stack/databases.py", line 1)
from the position of previous step being at database.py the control flow encounters from main import db - that means it should turn back to the main(main.py) module!
control flow returned to the main module start scanning again from the 1st line - till it finds from databases import User again. This reflects the traceback's 3rd step (File "/home/paulsteven/stack/main.py", line 3)
and you run into cycle ...
What is right way to solve the issue?
Keep all DB context/DB models in separate module(s).
Follow the sequence of objects relations and how they depend on each other:
---> Application instantiated first (app)
---> then DB framework instance is created db = SQLAlchemy(app) depending on app
---> then custom DB models created (like User(db.Model)) depending on db instance
main.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
# from databases import User <--- shouldn't be here
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/data_log.db'
db = SQLAlchemy(app)
if __name__ == '__main__':
from databases import User # after instantiating `db` import model(s)
db.create_all()
app.run(host='0.0.0.0', port=5001)
I got the below file structure for a Python-Flask app with flask-migrate :
My issues are
1-I'm unable to use db and create_app inside manage.py
When I do:
$ python manage.py db init
I got below error:
File "/app/main/model/model.py", line 25, in <module>
class User(db.Model):
NameError: name 'db' is not defined
(db is defined in main.init.py )
I have tried different options with no success.
I want to keep the manage.py , model.py and main.init.py in separate files.
2- In model .py I will need db .How will I make db available to model.py ?
Here below is manage.py
# This file take care of the migrations
# in model.py we have our tables
import os
import unittest
from flask_migrate import Migrate, MigrateCommand
from flask_script import Manager
from app.main import create_app
from app.main import db
# # We import the tables into the migrate tool
from app.main.model import model
app = create_app(os.getenv('BOILERPLATE_ENV') or 'dev')
app.app_context().push()
manager = Manager(app)
migrate = Migrate(app, db)
manager.add_command('db', MigrateCommand)
#### If I add model.py here all should be easier , but still I have the
#### issue with
#### from app.main import create_app , db
#manager.command
def run():
app.run()
#manager.command
def test():
"""Runs the unit tests."""
tests = unittest.TestLoader().discover('app/test', pattern='test*.py')
result = unittest.TextTestRunner(verbosity=2).run(tests)
if result.wasSuccessful():
return 0
return 1
if __name__ == '__main__':
manager.run()
This is app.init.py where db and create_app are defined
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
from flask_cors import CORS
from .config import config_by_name
from flask_restful import Resource, Api
# from flask_restplus import Resource
from app.main.controller.api_controller import gconnect, \
showLogin, createNewTest, getTest, getTests, getIssue, createNewIssue
db = SQLAlchemy()
flask_bcrypt = Bcrypt()
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config_by_name[config_name])
cors = CORS(app,
supports_credentials=True,
resources={r"/api/*":
{"origins":
["http://localhost:3000",
"http://127.0.0.1:3000"]}})
api = Api(app)
db.init_app(app)
flask_bcrypt.init_app(app)
api.add_resource(gconnect, '/api/gconnect')
api.add_resource(showLogin, '/login')
api.add_resource(createNewTest, '/api/test')
api.add_resource(getTest, '/api/test/<int:test_id>')
api.add_resource(getTests, '/api/tests')
api.add_resource(getIssue, '/api/issue/<int:issue_id>')
api.add_resource(createNewIssue, '/api/issue')
return app
And this is (just one of the table for simplicity) of my model
from sqlalchemy import Column, ForeignKey, Integer, String, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, backref
from sqlalchemy import create_engine
from sqlalchemy.sql import func
# # # This will let sql alchemy know that these clasess
# # # are special Alchemy classes
# Base = declarative_base()
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(250), nullable=False)
email = db.Column(db.String(250), nullable=False)
pictures = db.Column(db.String(250))
role = db.Column(db.String(25), nullable=True)
My issues are:
1-I'm unable to use db and create_app inside manage.py
When I do:
$ python manage.py db init
I got below error:
File "/app/main/model/model.py", line 25, in
class User(db.Model):
NameError: name 'db' is not defined
(db is defined in main.init.py )
I have tried different options with no success.
I want to keep the manage.py , model.py and main.init.py in separate files.
2- In model .py I will need db .How will I make db available to model.py ?
A simple solution is to create a seperate initializtions file besides your __init__.py. e.g. init.py where you initialize sqlalchemy along with other extensions. That way they can be imported in all the modules without any circular dependencies problems.
A more elegant solution however is to you use Flask's current_app and g proxies. They were made to help Flask users circumvent any problems with circular dependencies.
Typically you initalize the flask app in the __init__.py module and the __init__.py module sometimes has to import some variables from its sub-modules. This becomes problematic when sub-modules try to import initalized extensions
As a general rule of thumb, outer modules should be importing from their submodules not the other way around.
So here's one way you can solve your problem (cited from here):
** __init__.py
from flask import g
def get_db():
if 'db' not in g:
g.db = connect_to_database()
return g.db
#app.teardown_appcontext
def teardown_db():
db = g.pop('db', None)
if db is not None:
db.close()
def init_db():
db = get_db()
Now you can easily import your db connection into any other module by:
from flask import g
db = g.db
db.do_something()
I've got a problem with my simple app in Flask. I want to write a registration page, that connect with datebase by SQLAlchemy. I've got app.py file that look like this:
import flask
import settings
import os
from flask_sqlalchemy import SQLAlchemy
# Views
from main import Main
from login import Login
from remote import Remote
from register import Register
#from models import User
#from models import Task
app = flask.Flask(__name__)
app.config['SESSION_TYPE'] = 'filesystem'
app.secret_key = os.urandom(24)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////database.db'
db = SQLAlchemy(app)
# Routes
app.add_url_rule('/',
view_func=Main.as_view('main'),
methods=["GET"])
app.add_url_rule('/<page>/',
view_func=Main.as_view('page'),
methods=["GET"])
app.add_url_rule('/login/',
view_func=Login.as_view('login'),
methods=["GET", "POST"])
app.add_url_rule('/remote/',
view_func=Remote.as_view('remote'),
methods=['GET', 'POST'])
app.add_url_rule('/register/',
view_func=Register.as_view('register'),
methods=['GET', 'POST'])
#app.errorhandler(404)
def page_not_found(error):
return flask.render_template('404.html'), 404
app.debug = True
app.run()
So, as you can see I have URL rules in this file and now I want to use db variable in Register view, so I need import it there, my code for this file looks like this:
import flask, flask.views
from app import db
class Register(flask.views.MethodView):
def get(self):
return flask.render_template('register.html')
def post(self):
new_user = User(username = request.form['username'],
password = request.form['passwd']
db.session.add(new_user)
db.session.commit()
return flask.redirect(flask.url_for('index'))
In that case I get error, cause I have "tied" references, in app file is:
from register import Register
but in Register file is
from app import db
So it, obviously can't work, my solutions is to add URL rule in Register file. But I don't know how. Could you anyone help me?
Sorry for my confusing title, but I just getting started with Flask and I dnon't know how to name it.
You need to move your db assignment to before the imports that expect db to exist:
import flask
import settings
import os
from flask_sqlalchemy import SQLAlchemy
app = flask.Flask(__name__)
app.config['SESSION_TYPE'] = 'filesystem'
app.secret_key = os.urandom(24)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////database.db'
db = SQLAlchemy(app)
# Views
# These can now use
# from app import db
# because that line has executed above
from main import Main
from login import Login
from remote import Remote
from register import Register
#from models import User
#from models import Task
Now when you import the register module and the from app import db line runs, the db variable actually exists in the app module.
Note that you cannot now use app.py as the main script; if you use python app.py then it'll be imported as the __main__ module instead of app module, and using from app import db will create a new module, separate from the main script.
Either use a separate serving script, like serve.py and move your app.run() call into that file:
from app import app
app.debug = True
app.run()
or use from __main__ import db.
You also are missing a closing ) in your register module:
new_user = User(username = request.form['username'],
password = request.form['passwd']
# ^ ^
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/