Flask-SQLALchemy
Flask-SQLALchemy 是一个给你的应用添加 SQLALchemy 支持的 Flask 扩展。
它需要 SQLAlchemy 0.6 或更高的版本。它致力于简化在 Flask 中 SQLAlchemy 的 使用,提供了有用的默认值和额外的助手来更简单地完成日常任务。
我的conda源没有,我就直接pip3
数据库连接:
1. 与sqlalchemy一样,定义好数据库连接字符串DB_URI。
2. 将这个定义好的数据库连接字符串DB_URI,通过SQLALCHEMY_DATABASE_URI这个键放到app.config中。示例代码:app.config["SQLALCHEMY_DATABASE_URI"] = DB_URI.
3. 使用flask_sqlalchemy.SQLAlchemy类定义一个对象,并将app传入进去。示例代码:db = SQLAlchemy(app)。
创建ORM模型:
1. 与使用sqlalchemy一样,定义模型。现在不需要使用delarative_base来创建一个基类,而是使用db.Model来作为基类。
2. 在模型类中,Column、String、Integer以及relationship等,都不需要导入了,直接使用db下面相应的属性名就可以了。
3. 在定义模型的时候,可以不写__tablename__,那么flask_sqlalchemy会默认使用如果不设置该属性,类名小写作表名,
并且如果这个模型的名字使用多个单词且是驼峰命名法,则多个单词之间使用下划线来进行连接。
将ORM模型映射到数据库:
1. db.drop_all()
2. db.create_all()
使用session:
session也不需要使用sessionmaker来创建了。直接使用db.session且操作方式与SQLAl相同
查询数据:
如果查找数据只是查找一个模型上的数据,可以直接通过模型.query的方式进行查找。query就跟之前的sqlalchemy中的query方法是一样用的。
from flask import Flask from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) HOSTNAME = '127.0.0.1' PORT = '3306' DATABASE = 'flask_sqlalchemy' USERNAME = 'root' PASSWORD = 'root' # dialect+driver://username:password@host:port/database DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{db}?charset=utf8mb4".format(username=USERNAME,password=PASSWORD,host=HOSTNAME,port=PORT,db=DATABASE) app.config['SQLALCHEMY_DATABASE_URI'] = DB_URI app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db = SQLAlchemy(app) # 明言胜于暗喻 class User(db.Model): #__tablename__ = 'user' 如果不设置该属性,类名小写作表名 id = db.Column(db.Integer,primary_key=True,autoincrement=True) username = db.Column(db.String(50),nullable=False) def __repr__(self): return "<User(username: %s)>" % self.username class Article(db.Model): __tablename__ = 'article' id = db.Column(db.Integer,primary_key=True,autoincrement=True) title = db.Column(db.String(50),nullable=False) uid = db.Column(db.Integer,db.ForeignKey("user.id")) author = db.relationship("User",backref="artiles") db.drop_all() db.create_all() user = User(username='wqbin') article = Article(title='title one') sess=db.session article.author = user sess.add(article) sess.commit() users = User.query.order_by(User.id.desc()).all() print(users) user = User.query.filter(User.username=='wqbin').first() user.username='wqbin1' sess.commit() @app.route('/') def hello_world(): return 'Hello World!' if __name__ == '__main__': app.run()
alembic
SQLAlchemy该ORM框架本身没有带数据库版本控制功能,但是进行开发过程中难免修改数据模型,添加表,修改字段,都需要手动修改。于是两个解决该缺陷的两个工具应运而生,alembic与SQLAlchemy-Migrate。
alembic是sqlalchemy的作者开发的,Alembic 使用 SQLAlchemy 作为数据库引擎,为关系型数据提供创建、管理、更改和调用的管理脚本,协助开发和运维人员在系统上线后对数据库进行在线管理,主要用来做ORM模型与数据库的迁移与映射。
alembic使用方式跟git类似,表现在两个方面
- alembic的所有命令都是以alembic开头;
- alembic的迁移文件也是通过版本进行控制的。
以下将解释alembic的用法:
- 初始化alembic仓库:在终端中cd到项目目录中,然后执行命令alembic init alembic,创建alembic的仓库。
- 创建模型类:创建一个models.py模块,然后在里面定义模型类。
- 修改配置文件alembic.init
- 修改env.py
- 自动生成迁移文件:使用alembic revision --autogenerate-m "message"将当前模型中的状态生成迁移文件。
- 更新数据库:使用alembic upgrade head将刚刚生成的迁移文件,真正映射到数据库中。同理,如果要降级,那么使用alembic downgrade head。
- 修改代码后,重复4~5的步骤。
细节如下:
1.alembic init alembic
2.alembic.init
from sqlalchemy import Column,String,Integer,create_engine from sqlalchemy.ext.declarative import declarative_base DB_USERNAME = 'root' DB_PASSWORD = 'root' DB_HOST = '127.0.0.1' DB_PORT = '3306' DB_NAME = 'alembic' DB_URI = 'mysql+pymysql://%s:%s@%s:%s/%s?charset=utf8' % (DB_USERNAME,DB_PASSWORD,DB_HOST,DB_PORT,DB_NAME) engine = create_engine(DB_URI) Base = declarative_base(engine) class User(Base): __tablename__ = 'user' id = Column(Integer,primary_key=True,autoincrement=True) username = Column(String(50),nullable=False) country = Column(String(50))
3.修改配置文件alembic.init
【alembic】 sqlalchemy.url = driver://user:pass@localhost/dbname 改成: 【alembic】 sqlalchemy.url = mysql+pymysql://root:root@localhost/alembic?charset=utf8
4.修改env.py
from __future__ import with_statement from logging.config import fileConfig from sqlalchemy import engine_from_config from sqlalchemy import pool from alembic import context import sys,os sys.path.append(os.path.dirname(os.path.dirname(__file__))) import models # this is the Alembic Config object, which provides access to the values within the .ini file in use. config = context.config # Interpret the config file for Python logging. This line sets up loggers basically. fileConfig(config.config_file_name) # add your model's MetaData object here for 'autogenerate' support from myapp import mymodel target_metadata = mymodel.Base.metadata target_metadata = models.Base.metadata
5.使用alembic revision --autogenerate-m "aessage"将当前模型中的状态生成迁移文件
在flask中进行数据库迁移时报错,报错信息为"Target database is not up",解决方案如下:
方法一:(网上看的没找到对应)
- 找到alembic(数据库中的数据表)的最新版本号,找到文件夹verisons下的最新版本,文件名即为最新版本号(去掉末尾的_)。
- 然后更新数据库表alembic_version里version_num的字段,将该字段的值改为最新版本号
- 再次迁移即可成功
方法二:
- 删除数据库表alembic_version表和versions下面所有版本文件
- 重新输入revision命令
message.py文件内容:
5.更新数据库:使用alembic upgrade head将刚刚生成的迁移文件,真正映射到数据库中
6.重复:修改文件增加字段,更新数据库
class User(Base): __tablename__ = 'user' id = Column(Integer,primary_key=True,autoincrement=True) username = Column(String(50),nullable=False) country = Column(String(50)) city = Column(String(50))
几点补充:
PS C:\Users\WQBin\Desktop\python_flask\> alembic heads dda532d698cb (head) PS C:\Users\WQBin\Desktop\python_flask\> alembic upgrade 7ddd5756b83f INFO [alembic.runtime.migration] Context impl MySQLImpl. INFO [alembic.runtime.migration] Will assume non-transactional DDL. ERROR [alembic.util.messaging] Destination 7ddd5756b83f is not a valid upgrade target from current head(s) FAILED: Destination 7ddd5756b83f is not a valid upgrade target from current head(s) PS C:\Users\WQBin\Desktop\python_flask\> alembic downgrade dda532d698cb INFO [alembic.runtime.migration] Context impl MySQLImpl. INFO [alembic.runtime.migration] Will assume non-transactional DDL.
当head指针和current指针不对齐,不及时做upgrade 和downgrade,再做一些其他操作则会报错。
经典错误
1. FAILED: Target database is not up to date.
原因:主要是heads和current不相同。current落后于heads的版本。
解决办法:将current移动到head上。alembic upgrade head
2. FAILED: Can't locate revision identified by 'xxxxxx'
原因:数据库中存的版本号不在迁移脚本文件中
解决办法:删除数据库的alembic_version表中的数据,重新执行alembic upgrade head
3. 执行`upgrade head`时报某个表已经存在的错误:
原因:执行这个命令的时候,会执行所有的迁移脚本,因为数据库中已经存在了这个表。然后迁移脚本中又包含了创建表的代码。
解决办法:(1)删除versions中所有的迁移文件。(2)修改迁移脚本中创建表的代码
常用参数介绍:
init:创建一个alembic仓库 revision:创建一个新的版本文件 --autogenerate:自动将当前模型的修改,生成迁移脚本 -m:本次迁移做了哪些修改,用户可以指定这个参数,方便回顾 upgrade:将指定版本的迁移文件映射到数据库中,会执行版本文件中的upgrade函数。如果有多个迁移脚本没有被映射到数据库中,那么会执行多个迁移脚本 [head]:代表最新的迁移脚本的版本号 downgrade:会执行指定版本的迁移文件中的 down grade函数 heads:展示head指向的脚本文件版本号 history:列出所有的迁移版本及其信息 curent:展示当前数据库中的版本号
Flask-Script:
[flask-script 不再维护了,官网推荐使用flask自带cli]
简介与安装
Flask-Script的作用是可以通过命令行的形式来操作Flask,Flask-Script的作用从某种意义上来说是为了更好的管理项目,它通过一个manager来作为脚本控制整个项目的各个小部分点。
例如通过命令跑开发版本的服务器、设置数据库,定时任务等。 Flask-Script和Flask本身的工作方式类似,只需要定义和添加能从命令行中被Manager实例调用的命令即可。
Flask的开发Web服务器支持很多启动设置选项,但只能在脚本中作为参数传给app.run()函数。这种方式很不方便,传递设置选项的理想方式是使用命令行参数。
Flask-Scrip就是这么一个Flask扩展,为Flask程序添加一个命令行解析器。Flask-Script自带了一组常用选项,而且还支持自定义命令。
定义命令的三种方法
- 使用@command 装饰器
- 使用类继承自Command类
- 使用option装饰器
1.使用@command 装饰器
把脚本命令代码放在一个叫做manage.py文件中,然后在终端运行python manage.py hello命令,就可以看到输出hello。
from flask_script import Manager from flask_script_app import app manager = Manager(app) @manager.command def greet(): print('你好') if __name__ == '__main__': manager.run()
2.使用类继承自Command类
使用类的方式,有三点需要注意:
- 必须继承自Command基类
- 必须实现run方法
- 必须通过add_command 方法添加命令
from flask_script import Manager,Command manager = Manager(app) class SayHi(Command): def run(self): print('你好from [继承command的类方法]') manager.add_command("sayhi", SayHi)
3.使用option装饰器:如果想要在使用命令的时候还传递参数进去,那么使用@option 装饰器更加的方便:
@manager.option("-u","--username",dest="username") @manager.option("-e","--email",dest="email") def add_user(username,email): print(username,email)
小案例:
from flask import Flask from flask_sqlalchemy import SQLAlchemy import config app = Flask(__name__) app.config.from_object(config) db = SQLAlchemy(app) class BackendUser(db.Model): __tablename__ = 'backend_user' id = db.Column(db.Integer,primary_key=True,autoincrement=True) username = db.Column(db.String(50),nullable=False) email = db.Column(db.String(50),nullable=False) db.create_all() @app.route('/') def hello_world(): return 'Hello World!' if __name__ == '__main__': app.run()