12

I'd like to use Flask-Migrate and am looking at their example:

from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.script import Manager
from flask.ext.migrate import Migrate, MigrateCommand

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'

db = SQLAlchemy(app)
migrate = Migrate(app, db)

manager = Manager(app)
manager.add_command('db', MigrateCommand)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(128))

if __name__ == '__main__':
    manager.run()

This works great as a simple play example, but I have more than a single model and I don't want to define the models in both this script and the one that defines my application code. Thus, I want to pull them into a model file that I can share between the two.

I attempting to do this, by placing the User class into a models.py and then importing User from there. Unfortunately, that throws a NameError: name 'db' is not defined.

My question(s) are:

  • Do I need to use db = SQLAlchemy(app) in my models.py, and if so will this be available in both my migration script and the flask application itself?
  • If I can't (or shouldn't) put it in models.py, how do I utilize my models in their own file without passing db all over?
NewGuy
  • 3,273
  • 7
  • 43
  • 61
  • Check this out https://realpython.com/blog/python/python-web-applications-with-flask-part-ii/ – copser Dec 15 '15 at 14:10

3 Answers3

21

Partitioning such a small application into modules is tricky, because you find a lot of cases where the two modules that you create need to mutually import each other, creating circular dependencies.

I recommend that you look at how you can structure a larger application properly, using an app factory function and delayed initialization of all extensions. An example application that does this is the Flasky app featured in my book.

All that said, it is possible to separate the application into two parts, you just need to be careful with where you place the import statements. In the example below, I decided to move the creation of the db instance and the User model into a models.py file.

Here is the main application's module:

from flask import Flask
from flask.ext.script import Manager
from flask.ext.migrate import Migrate, MigrateCommand

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'

from models import db  # <-- this needs to be placed after app is created
migrate = Migrate(app, db)

manager = Manager(app)
manager.add_command('db', MigrateCommand)

if __name__ == '__main__':
    manager.run()

And here is models.py:

from __main__ import app
from flask.ext.sqlalchemy import SQLAlchemy

db = SQLAlchemy(app)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(128))

Here the main module will create app, and only then will import models.py. When models.py tries to import app from the main module, it has already been created. If you move from models import db to the top of the file with the other imports this code breaks.

Miguel Grinberg
  • 65,299
  • 14
  • 133
  • 152
  • 7
    I understand your example but what if what I want is to split models.py into different .pys? for example User.py, Foo.py, Bar.py. I'm struggling to find a way to do that. – Vitor Hugo Apr 01 '17 at 03:41
  • 1
    @VitorHugo you can create a `models` package, create `db` instance in the package's `__init__.py` file, then add one module per model, with all these modules importing the `db` instance as `from . import db`. – Miguel Grinberg Apr 01 '17 at 15:21
  • That worked but when I do a db.create_all() on the main application module after the 'from models import db' it does not create the tables which is what happens when I do models.py. Should that be a separated thing or should I worry about it? – Vitor Hugo Apr 01 '17 at 18:54
  • 2
    You have to import all the models before you call `db.create_all()`. – Miguel Grinberg Apr 02 '17 at 03:35
  • I thought there would be some cyclical problem and not work but it did. Thank you very much sir. – Vitor Hugo Apr 03 '17 at 13:24
  • To use the `User` model is it also necessary to `from models import User` in the main module? – Philip O'Brien Oct 11 '17 at 15:52
  • 1
    Yes, you would also import `User` in the same place where `db` is imported. – Miguel Grinberg Oct 11 '17 at 17:31
  • 3
    It would be great to see the code for the working solution with a `models` package. – Fuhrmanator May 16 '18 at 15:21
  • I Know the answer is old, but I just had a problem with it: it doesn't work on production servers, such as unicorn, since each app is not in "__main__". Looking for an answer now... – Tiago Duque Jul 23 '20 at 20:49
2

Here is an idea. You can create a package model in which you define classes as individual .py files. For example, user.py contains the User class.

In the __init__.py file you can define the db variable. You may also include from user import User statement so that it would be possible to access User object directly outside the package. For example you can import the model package somewhere else in the program using import model and then use model.User to directly use the User class.

To use db variable directly in the user.py use from ..model import db which imports db variable defined in the __init__.py file.

Andy
  • 49,085
  • 60
  • 166
  • 233
aliasm2k
  • 883
  • 6
  • 12
  • Doesn't this mean that I then need to pass `app` (from `db = SQLAlchemy(app)`) around instead of `db`? – NewGuy Dec 15 '15 at 06:44
  • Well, in that case you can add both `app` and `db` in `__init__.py` – aliasm2k Dec 15 '15 at 08:06
  • 2
    @aliasm2k The whole point of the `model` module is to seperate model from app. Putting them both in package __init__ provides no looser coupling than keeping them in the single module. – John May 16 '18 at 22:07
0

Your answer was very helpful for me. I finally grasped the rather cumbersome python importing rules. Here I try to illustrate the pattern in a cleaner way (by "cleaner" I mean not complicated with additional issues, like those on migration). This is the simple and common case of partitioning and app into a main module and a secondary module which just declare the database models.

main:

from flask import Flask, render_template, request, url_for, redirect
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__) # this is a key point

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///clientes.sqlite3'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False 
app.config['SECRET_KEY'] = "random string"

from fasiDB import db, Corr, Cliente, Producto # db and models

# here come the various routes which access db

models:

from __main__ import app
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy(app) # the argument "app" comes from main

class Corr(db.Model):
    id = db.Column('corr_id', db.Integer, primary_key=True)
    nf = db.Column(db.Integer)

...

class Cliente(db.Model):
   id = db.Column('cliente_id', db.Integer, primary_key = True)
   nombre = db.Column(db.String(64), unique=True)
   rSocial = db.Column(db.String(64), unique=True)
   ruc = db.Column(db.String(27), unique=True)
   dir = db.Column(db.String(100))

...

René
  • 21
  • 4