在Flask-Security中结合使用Flask-Migrate
我正在尝试让一个基本的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_datastore
和security
时可能做错了,因为我可能不应该以那种方式导入User
和Role
,但我不太确定该如何正确访问它们。
编辑:
我根据建议添加了这个最终命令:
python db_manager.py db ugrade
但是,现在当我尝试通过电子邮件确认用户注册时,出现了这个错误:
(InvalidRequestError: 对象''已经附加到会话'1'(这是'3')
1 个回答
使用Flask-Migrate和Alembic的工作流程如下:
db init
这个步骤你只需要在创建迁移仓库时做一次,以后就不需要再做了。
db migrate
你运行这个命令来生成一个迁移脚本。命令的输出会告诉你迁移脚本在哪里创建,并给出一个总结,说明里面包含了什么内容。在这个阶段,你的数据库还没有被修改。
检查迁移脚本
这一步非常重要。自动生成的迁移并不完美,你需要仔细检查生成的脚本,并做必要的修改。
db upgrade
这个命令会把迁移应用到你的数据库,实际上就是进行必要的结构更改。
现在你可以使用你的数据库了。当你对模型做更多更改时,回到第二步,重复这个流程。
根据你的描述,你可能漏掉了第4步,也就是upgrade
这个调用。
另外,你的两个脚本之间有一些重复的部分,建议你尝试合并它们。可以看看别人是如何将Flask应用拆分成多个模块或包的。