SQLAlchemy create_all() does not create tables
You should put your model class before create_all()
call, like this:
from flask import Flaskfrom flask_sqlalchemy import SQLAlchemyapp = Flask(__name__)app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql+psycopg2://login:pass@localhost/flask_app'db = SQLAlchemy(app)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 '<User %r>' % self.usernamedb.create_all()db.session.commit()admin = User('admin', 'admin@example.com')guest = User('guest', 'guest@example.com')db.session.add(admin)db.session.add(guest)db.session.commit()users = User.query.all()print users
If your models are declared in a separate module, import them before calling create_all()
.
Say, the User
model is in a file called models.py
,
from flask import Flaskfrom flask_sqlalchemy import SQLAlchemyapp = Flask(__name__)app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql+psycopg2://login:pass@localhost/flask_app'db = SQLAlchemy(app)# See important note belowfrom models import Userdb.create_all()db.session.commit()admin = User('admin', 'admin@example.com')guest = User('guest', 'guest@example.com')db.session.add(admin)db.session.add(guest)db.session.commit()users = User.query.all()print users
Important note: It is important that you import your models after initializing the db
object since, in your models.py
_you also need to import the db
object from this module.
This is probably not the main reason why the create_all()
method call doesn't work for people, but for me, the cobbled together instructions from various tutorials have it such that I was creating my db in a request context, meaning I have something like:
# lib/db.pyfrom flask import g, current_appfrom flask_sqlalchemy import SQLAlchemydef get_db(): if 'db' not in g: g.db = SQLAlchemy(current_app) return g.db
I also have a separate cli command that also does the create_all:
# tasks/db.pyfrom lib.db import get_db@current_app.cli.command('init-db')def init_db(): db = get_db() db.create_all()
I also am using a application factory.
When the cli command is run, a new app context is used, which means a new db is used. Furthermore, in this world, an import model in the init_db method does not do anything, because it may be that your model file was already loaded(and associated with a separate db).
The fix that I came around to was to make sure that the db was a single global reference:
# lib/db.pyfrom flask import g, current_appfrom flask_sqlalchemy import SQLAlchemydb = Nonedef get_db(): global db if not db: db = SQLAlchemy(current_app) return db
I have not dug deep enough into flask, sqlalchemy, or flask-sqlalchemy to understand if this means that requests to the db from multiple threads are safe, but if you're reading this you're likely stuck in the baby stages of understanding these concepts too.
If someone is having issues with creating tables by using files dedicated to each model, be aware of running the "create_all" function from a file different from the one where that function is declared.So, if the filesystem is like this:
Root --app.py <-- file from which app will be run--models----user.py <-- file with "User" model----order.py <-- file with "Order" model----database.py <-- file with database and "create_all" function declaration
Be careful about calling the "create_all" function from app.py.
This concept is explained better by the answer to this thread posted by @SuperShoot