SQLAlchemy create_all() unable to create new tables - python

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()

Related

Flask objects on multiple sessions when they not should be

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.

flask-migrate issue while refactoring code

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()

Using SQLAlchemy models in and out of Flask

I'm trying to build SQLAlchemy models that can be used in Flask and in other non-Flask services. I know that in order to use these objects in Flask I can use the Flask-SQLAlchemy module and build the models like this:
app_builder.py
def create_app(config):
# Create a Flask app from the passed in settings
app = Flask('web_service')
app.config.from_object(config)
# Attach SQLAlchemy to the application
from database import db
db.init_app(app)
database.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Job(db.Model):
__tablename__ = 'job'
job_id = db.Column(db.Integer, primary_key=True)
description = db.Column(db.String(256))
def __init__(self, description):
self.description = description
However it looks like doing this ties the models to using flask_sqlalchemy. I have another service that I would like to use these models in that don't use flask. Is there a way I can reuse these class definitions (maybe by changing db.Model) within a non-Flask specific context?
flask_sqlalchemy doesn`t allow you to use it outside of a Flask context. However, you can create models via SQLAlchemy itself. So your database.py file would look like this:
from sqlalchemy import MetaData, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
metadata = MetaData()
Base = declarative_base(metadata=metadata)
class Job(Base):
__tablename__ = 'job'
job_id = Column(Integer, primary_key=True)
description = Column(String(256))
def __init__(self, description):
self.description = description
You can initialize a flask_sqlalchemy object using produced metadata (flaskdb.py):
from flask_sqlalchemy import SQLAlchemy
from database import metadata
db = SQLAlchemy(metadata=metadata)
And you initialize your Flask app like this:
from flask import Flask
from flaskdb import db
def create_app(config):
app = Flask('web_service')
app.config.from_object(config)
db.init_app(app)
Created models can be used outside of the Flask context via a Session. For example:
from sqlalchemy import create_engine
from sqlalchemy.orm import Session
from database import metadata, Job
engine = create_engine('your://database#configuration/here')
session = Session(engine)
jobs = session.query(Job).all()
session.close()
As a downside of this approach, you can't use direct access to database objects through models. Instead, you are forced to use Sessions:
from database import Job
from flaskdb import db
Job.query.all() # Does not work
db.session.query(Job).all() # Works

db.create_all() doesn't create tables defined in separate file

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.

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