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 = ''
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类似,表现在两个方面

  1. alembic的所有命令都是以alembic开头;
  2. alembic的迁移文件也是通过版本进行控制的。

以下将解释alembic的用法:

  1. 初始化alembic仓库:在终端中cd到项目目录中,然后执行命令alembic init alembic,创建alembic的仓库。
  2. 创建模型类:创建一个models.py模块,然后在里面定义模型类。
  3. 修改配置文件alembic.init
  4. 修改env.py
  5. 自动生成迁移文件:使用alembic revision --autogenerate-m "message"将当前模型中的状态生成迁移文件。
  6. 更新数据库:使用alembic upgrade head将刚刚生成的迁移文件,真正映射到数据库中。同理,如果要降级,那么使用alembic downgrade head。
  1. 修改代码后,重复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 = ''
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",解决方案如下:

方法一:(网上看的没找到对应)

  1. 找到alembic(数据库中的数据表)的最新版本号,找到文件夹verisons下的最新版本,文件名即为最新版本号(去掉末尾的_)。
  2. 然后更新数据库表alembic_version里version_num的字段,将该字段的值改为最新版本号
  3. 再次迁移即可成功

方法二:

  1. 删除数据库表alembic_version表和versions下面所有版本文件
  2. 重新输入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类

使用类的方式,有三点需要注意:

  1. 必须继承自Command基类
  2. 必须实现run方法
  3. 必须通过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()

flask_script_app.py

DB_USERNAME = 'root'
DB_PASSWORD = 'root'
DB_HOST = '127.0.0.1'
DB_PORT = ''
DB_NAME = 'flask_script' DB_URI = 'mysql+pymysql://%s:%s@%s:%s/%s?charset=utf8' % (DB_USERNAME,DB_PASSWORD,DB_HOST,DB_PORT,DB_NAME) SQLALCHEMY_DATABASE_URI = DB_URI
SQLALCHEMY_TRACK_MODIFICATIONS = False

config.py

from flask_script import Manager

db_manager = Manager()

@db_manager.command
def init():
print('迁移仓库创建完毕!') @db_manager.command
def revision():
print('迁移脚本生成成功!') @db_manager.command
def upgrade():
print('脚本映射到数据库成功!')

db_script.py

from flask_script import Manager,Command
from flask_script_app import app,BackendUser,db
from db_script import db_manager manager = Manager(app)
manager.add_command("db",db_manager) @manager.command
def greet():
print('你好') manager = Manager(app)
class SayHi(Command): def run(self):
print('你好from [继承command的类方法]') manager.add_command("sayhi", SayHi) @manager.option("-u","--username",dest="username")
@manager.option("-e","--email",dest="email")
def add_user(username,email):
user = BackendUser(username=username,email=email)
db.session.add(user)
db.session.commit() if __name__ == '__main__':
manager.run()

manage.py

 python manage.py db --help
usage: Perform database migrations Perform database migrations positional arguments:
{init,revision,migrate,edit,merge,upgrade,downgrade,show,history,heads,branches,current,stamp}
init Creates a new migration repository
revision Create a new revision file.
migrate Alias for 'revision --autogenerate'
edit Edit current revision.
merge Merge two revisions together. Creates a new migration
file
upgrade Upgrade to a later version
downgrade Revert to a previous version
show Show the revision denoted by the given symbol.
history List changeset scripts in chronological order.
heads Show current available heads in the script directory
branches Show current branch points
current Display the current revision for each database.
stamp 'stamp' the revision table with the given revision;
don't run any migrations optional arguments:
-?, --help show this help message and exit

Flask-Migrate

在实际的开发环境中,经常会发生数据库修改的行为。一般我们修改数据库不会直接手动的去修改,而是去修改ORM对应的模型,然后再把模型映射到数据库中。

这时候如果有一个工具能专门做这种事情,就显得非常有用了,而flask-migrate就是做这个事情的。

flask-migrate是基于Alembic进行的一个封装,并集成到Flask中,而所有的迁移操作其实都是Alembic做的,他能跟踪模型的变化,并将变化映射到数据库中。

what is Flask-Migrate

Flask-Migrate is an extension that handles SQLAlchemy database migrations for Flask applications using Alembic.

The database operations are made available through the Flask command-line interface or through the Flask-Script extension.

Flask-Migrate是一个扩展,使用 Alembic 处理 Flask 程序的 SQLAlchemy 数据库迁移。数据库操作通过 Flask 命令行界面或 Flask-Script 扩展来提供。

Why Use Flask-Migrate vs. Alembic Directly?

Flask-Migrate is an extension that configures Alembic in the proper way to work with your Flask and Flask-SQLAlchemy application.

In terms of the actual database migrations, everything is handled by Alembic so you get exactly the same functionality.

案例讲解:

1.flask db init

add a migrations folder to your application. The contents of this folder need to be added to version control along with your other source files.

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db' db = SQLAlchemy(app)
migrate = Migrate(app, db) class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(128))

2.flask db migrate

生成app.db文件

3.最后添加将映射文件真正的映射到数据库中

4.假设model发生变化,我们重复2-3

flask db migrate  //生成version文件
flask db update //更新到新版本

经典案例:

 flask_migrate常用命令:
. 初始化一个环境:python manage.py db init
. 自动检测模型,生成迁移脚本:python manage.py db migrate
. 将迁移脚本映射到数据库中:python manage.py db upgrade
. python manage.py db --help

发现错误:

我看的教程中使用的python manage.py db xxx

但实际上是应该使用flask db 其中我遇到了两个错误

1.did not provide the "FLASK_APP" environment variable

>flask db init
报错如下:
Usage: flask-script.py db init [OPTIONS]
Error: Could not locate a Flask application. You did not provide the "FLASK_APP" environment variable, and a "wsgi.py" or "app.py" module was not found in the current directory.

两个方法一个是把flask入口声明 set  FLASK_APP=xxxx.py 另一个把flask入口重命名为app.py

2.KeyError: 'migrate'

>flask db init
报错如下:
File "D:\app\Anaconda\lib\site-packages\flask_migrate\__init__.py", line 125, in init
directory = current_app.extensions['migrate'].directory
KeyError: 'migrate'

网上的教程我都没成功:

方案一:https://blog.csdn.net/aimill/article/details/80867402

方案二:https://github.com/miguelgrinberg/Flask-Migrate/issues/196

第六章 Flask数据库(二)的更多相关文章

  1. 第六章 Flask数据库(一)之SQLAlchemy

    将ORM模型映射到数据库中 1. 用`declarative_base`根据`engine`创建一个ORM基类. from sqlalchemy.ext.declarative import decl ...

  2. 第六章_PHP数组(二)

    这篇随笔是对预定义数组变量的总结.通过预定义数组变量,我们可以获得系统环境.用户对话.表单数据等信息. 1.服务器变量:$_SERVER 利用foreach语句打印$_SERVER中的所有元素: &l ...

  3. java JDK8 学习笔记——第16章 整合数据库

    第十六章 整合数据库 16.1 JDBC入门 16.1.1 JDBC简介 1.JDBC是java联机数据库的标准规范.它定义了一组标准类与接口,标准API中的接口会有数据库厂商操作,称为JDBC驱动程 ...

  4. sql 入门经典(第五版) Ryan Stephens 学习笔记 (第六,七,八,九,十章,十一章,十二章)

    第六章: 管理数据库事务 事务 是 由第五章 数据操作语言完成的  DML ,是对数据库锁做的一个操作或者修改. 所有事务都有开始和结束 事务可以被保存和撤销 如果事务在中途失败,事务中的任何部分都不 ...

  5. 读《构建之法》一、二、十六章随笔a

    第一章    概论 “软件团队要从需求分析开始,把合适的需求梳理出来,然后逐步开展后续工作”:——p3 问题:好的用户体验要从软件分析开始,那么软件分析仅仅是从用户的需求出发吗? 我的看法:需求分析是 ...

  6. Flask 教程 第十六章:全文搜索

    本文翻译自The Flask Mega-Tutorial Part XVI: Full-Text Search 这是Flask Mega-Tutorial系列的第十六部分,我将在其中为Microblo ...

  7. c++ 吕凤翥 第六章 类和对象(二)

    c++ 吕凤翥 第六章 类和对象(二) 指针   引用  和数组 一:对象指针和对象引用 1.指向类的成员的指针 分为指向成员变量和指向成员函数两种指针 成员变量的格式:     类型说明符  类名: ...

  8. 2017.2.28 activiti实战--第六章--任务表单(二)外置表单

    学习资料:<Activiti实战> 第六章 任务表单(二)外置表单 6.3 外置表单 考虑到动态表单的缺点(见上节),外置表单使用的更多. 外置表单的特点: 页面的原样显示 字段值的自动填 ...

  9. 2017.2.7 开涛shiro教程-第六章-Realm及相关对象(二)

    原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398 根据下载的pdf学习. 第六章 Realm及相关对象(二) 1.Authenticatio ...

随机推荐

  1. 深入理解linux内核-内存寻址

    逻辑地址:由一个段和偏移量组成的地址线性地址(虚拟地址):物理地址:CPU的物理地址线相对应的地址32或36位 多处理器系统中每个CPU对应一个GDT 局部线程存储:用于线程内部的各个函数调用都能访问 ...

  2. CNN-2: AlexNet 卷积神经网络模型

    1.AlexNet 模型简介 由于受到计算机性能的影响,虽然LeNet在图像分类中取得了较好的成绩,但是并没有引起很多的关注. 知道2012年,Alex等人提出的AlexNet网络在ImageNet大 ...

  3. c# 中对于每次修改的程序 都必须重新手动生成 才能编译的问题

    问题描述:原来用VS2017,升级了VS2019,发现修改了Winform界面,F5运行竟然还是原来的界面 问题解决: 需要修改两个地方 工具>> 选项>> 项目和解决方案&g ...

  4. eclipse3.62开发第一个java程序HelloWorld

    [学习笔记] 用eclipse3.62开发第一个java程序: 使用eclipse之前们需要先配置一下jdk.window/preference/java/installed JREs,详细请见视频. ...

  5. go map的定义和使用 键值对存储

    定义map    var m map[string]int //定义map 初始化map    m = make(map[string]int) //初始化map 修改map中ok 的值  m[&qu ...

  6. Scratch—实现一个按钮的动画效果

    上次介绍了利用克隆体操作生成菜单按钮,今天讲一讲如何让一个按钮具有动画的效果:当鼠标移到某个按钮上面时,显示动画效果 让菜单按钮有虚像效果 让菜单具有逐渐变大的效果 改变菜单按钮的颜色 其他的特效都是 ...

  7. Apache2.4+Tomcat7.0+php5.5整合配置详解

    在上一篇的基础上,继续添加php的配置 一.首先下载php5.5 首先下载php5.5,到官网下载http://www.php.net/downloads.php,参考http://www.cnblo ...

  8. oracle查询十分钟之前的数据

    select * from TABLE as of timestamp sysdate - 10/1440 t WHERE ColName='1111'; TABLE:表名 WHERE:查询子句 sy ...

  9. 1、Java基础:面向对象六大原则

    本文主要介绍了面向对象六大原则. 单一职责原则(Single-Resposibility Principle). “对一个类而言,应该仅有一个引起它变化的原因.”本原则是我们非常熟悉地”高内聚性原则” ...

  10. 安装mysql采坑记录

    安装之前彻底卸载之前的mysql,再次安装,初始化数据库那一步失败. 再次彻底卸载mysql,把原先的安装路径的文件夹删除,文件夹路径:C:\ProgramData,再次安装,成功. 总结:重装mys ...