在Flask-Security中结合使用Flask-Migrate

1 投票
1 回答
929 浏览
提问于 2025-04-18 11:02

我正在尝试让一个基本的Flask-Security应用程序与Flask-Migrate一起工作。我有两个主要的.py文件:app.py和db_migrate.py。

app.py:

from flask import Flask, render_template, request, session
from flask.ext.babel import Babel
from flask.ext.mail import Mail
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.security import Security, SQLAlchemyUserDatastore, UserMixin, RoleMixin    
import os
basedir = os.path.abspath(os.path.dirname(__file__)) #should be __ file __ with no spaces

# Create app
app = Flask(__name__)
app.config['DEBUG'] = True
app.config['SECRET_KEY'] = 'super-secret'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'app.db')
app.config['DEFAULT_MAIL_SENDER'] = 'info@site.com'
app.config['SECURITY_REGISTERABLE'] = True
app.config['SECURITY_CONFIRMABLE'] = True
app.config['SECURITY_RECOVERABLE'] = True
app.config.from_object('config.email')

# Setup mail extension
mail = Mail(app)

# Setup babel
babel = Babel(app)

@babel.localeselector
def get_locale():
    override = request.args.get('lang')

    if override:
        session['lang'] = override

    rv = session.get('lang', 'en')
    return rv

# Create database connection object
db = SQLAlchemy(app)

# Setup Flask-Security
from db_manager import User, Role #THIS IS PROBABLY WRONG!

user_datastore = SQLAlchemyUserDatastore(db, User, Role)
security = Security(app, user_datastore)

#db.create_all()

# Views
@app.route('/')
def home():
    return render_template('index.html')

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

db_migrate.py:

from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.script import Manager
from flask.ext.migrate import Migrate, MigrateCommand
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.security import Security, SQLAlchemyUserDatastore, UserMixin, RoleMixin

import os
basedir = os.path.abspath(os.path.dirname(__file__))

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'app.db')

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

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

# Define models
roles_users = db.Table('roles_users',
        db.Column('user_id', db.Integer(), db.ForeignKey('user.id')),
        db.Column('role_id', db.Integer(), db.ForeignKey('role.id')))

class Role(db.Model, RoleMixin):
    id = db.Column(db.Integer(), primary_key=True)
    name = db.Column(db.String(80), unique=True)
    description = db.Column(db.String(255))

class User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(255), unique=True)
    password = db.Column(db.String(255))
    active = db.Column(db.Boolean())
    confirmed_at = db.Column(db.DateTime())
    favcolor = db.Column(db.String(255))
    roles = db.relationship('Role', secondary=roles_users,
                            backref=db.backref('users', lazy='dynamic'))

    def __str__(self):
        return '<User id=%s email=%s>' % (self.id, self.email)

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

我已经运行了迁移工具来初始化和迁移数据库一次,以创建一个新的数据库,这个过程是成功的:

python db_manager.py db init
python db_manager.py db migrate

我尝试运行app.py。它在本地服务器上正常工作,但当我尝试登录用户时,出现了以下的操作错误:

操作错误:没有这样的表:user u'SELECT user.id AS user_id, user.email AS user_email, user.password AS user_password, user.active AS user_active, user.confirmed_at AS user_confirmed_at, user.favcolor AS user_favcolor \nFROM user \nWHERE lower(user.email) LIKE lower(?)\n LIMIT ? OFFSET ?' (u'xxx@xxx.com', 1, 0)

基本上,我怀疑我在创建user_datastoresecurity时可能做错了,因为我可能不应该以那种方式导入UserRole,但我不太确定该如何正确访问它们。

编辑:

我根据建议添加了这个最终命令:

python db_manager.py db ugrade

但是,现在当我尝试通过电子邮件确认用户注册时,出现了这个错误:

(InvalidRequestError: 对象''已经附加到会话'1'(这是'3')

1 个回答

4

使用Flask-Migrate和Alembic的工作流程如下:

  1. db init

    这个步骤你只需要在创建迁移仓库时做一次,以后就不需要再做了。

  2. db migrate

    你运行这个命令来生成一个迁移脚本。命令的输出会告诉你迁移脚本在哪里创建,并给出一个总结,说明里面包含了什么内容。在这个阶段,你的数据库还没有被修改。

  3. 检查迁移脚本

    这一步非常重要。自动生成的迁移并不完美,你需要仔细检查生成的脚本,并做必要的修改。

  4. db upgrade

    这个命令会把迁移应用到你的数据库,实际上就是进行必要的结构更改。

  5. 现在你可以使用你的数据库了。当你对模型做更多更改时,回到第二步,重复这个流程。

根据你的描述,你可能漏掉了第4步,也就是upgrade这个调用。

另外,你的两个脚本之间有一些重复的部分,建议你尝试合并它们。可以看看别人是如何将Flask应用拆分成多个模块或包的。

撰写回答