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.
Related
I'm trying to develop a database driven flask app.
I have an api.py file which has the flask app, api and SQLAlchemy db objects and a users.py file which contains the routes ands code to create a database table.
In the users.py file, there's a UserManager Resource which has the routes. I have to add this resource to the API from this file.
So users.py needs to import the db and the api from the api.py file and api.py needs to serve the flask app so it has all the variables users.py needs, but api.py also needs to import users.py in order to use it as a flask blueprint.
api.py:
...
from user import user
app = Flask(__name__)
app.register_blueprint(user)
api = Api(app)
...
db = SQLAlchemy(app)
ma = Marshmallow(app)
...
if __name__ == '__main__':
app.run(debug=True)
users.py:
from api import api
user = Blueprint('user', __name__, template_folder='templates')
db = SQLAlchemy(api.app)
ma = Marshmallow(api.app)
class User(db.Model):
user_id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(120), unique=True, nullable=False)
username = db.Column(db.String(32), unique=True, nullable=False)
password = db.Column(db.String(32))
first_name = db.Column(db.String(32))
last_name = db.Column(db.String(32))
...
...
class UserManager(Resource):
#user.route('/get/<user_id>', methods = ['GET'])
def get_user(user_id):
...
This of course results in errors due to circular imports. Question is, how do I separate the flask files with the routes from the api file when the blueprint has dependencies from the api (the db and the api objects)
I also somehow have to do something like api.add_resource(UserManager, '/api/users') but I'm not sure where that'd go given the circular import.
Tried reducing dependencies between two files, but couldn't achieve described goal without doing a 2 way import.
Either trying to get 2 way import to work or still have same structure of separate files with routes but using 1 way import.
This is a well known problem in flask. The solution is to use application factories.
Cookiecutter Flask does this really well and offers a good template. It is well worth to check out their repo and try to understand what they are doing.
Assuming you have a folder app and this folder contains a file __init__.py and your other files user.py, etc.
Create a file app/extensions.py with this content and any other extension you need to initialize.
...
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
...
Import the db object (which was created, but not initialized) in the files where you need it.
from app.extensions import db
In your app/api.py file
from flask import Flask
from app.extensions import db
from app.user import user as user_bp
def create_app():
app = Flask(__name__)
register_extensions(app)
register_blueprints(app)
return app
def register_extensions(app):
"""Register Flask extensions."""
db.init_app(app)
return None
def register_blueprints(app):
"""Register Flask blueprints."""
app.register_blueprint(user_bp)
return None
if __name__ == '__main__':
app = create_app()
app.run(debug=True)
Add stuff based on this approach as needed.
I have flask web app which uses mysql db using flask-sqlalchemy.
I have kept separate utility scripts for DDL creation.
My existing app works perfectly fine but this script is unable to create new table.
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from config import connect_strng
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = connect_string
db = SQLAlchemy()
# Added this import just before create_all
from db_models import Test
db.create_all()
db.session.commit()
I have defined model inside db_models
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Test(db.Model):
__tablename__ = 'test'
test_id = db.Column(db.Integer, primary_key=True)
My script is finishing with exit code of 0 indicating no errors, but I don't see table getting generated in mysql database.
$ python create_table.py
$ echo $?
0
I checked answer to the similar question but did not work.
You need to use the same db object across your whole app. Importing it where it is needed.
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy() # <--- This is what is causing your issue
# IT should look something more like...
from create_table import db
class Test(db.Model):
__tablename__ = 'test'
test_id = db.Column(db.Integer, primary_key=True)
However there is a problem with the above suggestion...It will lead to a circular import. To solve that requires restructuring your app a bit. Exactly how to do it is up to you but I'll give you a suggestion.
Create a new file called database.py and put your db object in there. Then you can do from database import db whenever you need db.
database.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
As far as how to structure your app, consider the application factory pattern. It takes into account the circular import issue that commonly arises with flask.
I was able to resolve the issue by making use of flask's application context.
As sugested by #noslenkwah, you should use db object from single place by defining into single file database.py.
Here is my solution.
database.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
db_models.py
from database import db
class Test(db.Model):
__tablename__ = 'test'
test_id = db.Column(db.Integer, primary_key=True)
create_table.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from config import connect_strng
from database import db
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = connect_string
with app.app_context():
db.init_app(app)
# Added this import just beore create_all
from db_models import Test, CrawlStat
db.create_all()
db.session.commit()
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 am trying to create the tables for my models, which are defined in a separate module from my app. I call db.create_all(), but no tables are created and there are no errors. I've defined the models and imported them before calling create_all. Why doesn't this work?
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql+psycopg2://postgres:123#localhost/flask'
db = SQLAlchemy(app)
from models import User
db.create_all()
db.session.commit()
models.py:
from flask.ext.sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class User(db.Model):
__tablename__ = 'users'
uid = db.Column(db.Integer, primary_key = True)
You created two separate db instances, one along with the app and one along with the models. Each instance has it's own metadata that stores the tables defined on it. The one you're using to issue the create table statement was not the one that the models were defined on. You should use only one instance of the extension, importing it when needed.
myapp/__init__.py:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
db = SQLAlchemy(app)
# import the models *after* the db object is defined
from myapp import models
myapp/models.py:
from myapp import db
class User(db.Model):
...
create_tables.py:
from myapp import app, db
with app.app_context():
db.create_all()
Other things to note:
You should structure your app as a package, so that everything is importable under one location.
flask.ext is deprecated, import the extension directly from its package name.
Flask-SQLAlchemy automatically generates __tablename__ from the class name, you don't need to define it yourself.
You do not have to call commit after create_all.
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/