文章目录
pip list 查看安装了哪些库
pip freeze > 文件名.txt 将已经安装的第三方库导入到本地文件(项目路径中)
pip install -r 文件名 循环遍历导出的依赖库
标准的flask目录
- static:下面存放静态资源
- templates: 用于存放要选渲染的资源,一般为html (路径在flask程序的同级目录中) (render_template)
flask的helloworld快速上手
分析:
- 首先导入依赖
- 初始化flask返回给变量app
- 使用@app.route设置路由(编写函数,解析路由)
- 启动app
Flask函数常用参数
一般初始化函数时,使用Flask(name) => 这个__name__会解析到当前类,然后将当前类所在的包设为主项目(可以访问static templates)。
Flask(name,**)
Flask后还有三个常用的参数。
- static_folder=“文件夹名” => 将static文件夹名字进行更改。默认是static
- static_url_path = “/访问路径” => 将访问页面 localhost:5000/static/* 修改成 localhost:5000/修改后的访问路径/*
- template_folder = “文件夹名” => 将template文件夹名字进行更改。默认是template
路由请求类型methods
设置可以使用post请求访问
@app.route('/',methods=["Get","Post"])
路由参数,指定参数类型为int
@app.route("/order/<int:orderId>")
def getOrderId(orderId):
return "orderId : %s"%orderId
指定加载配置数据
- 从配置对象加载 from_object
- 从本地配置类中加载 from_pyfile
- 从环境变量加载 from_envvar
- app.confg[‘配置’] = “”
配置大写如:SECRET_KEY
- 设置配置 app.confg[‘配置’] = “”
获取的话,就是字典。如 - 查询配置
print(app.confg.get("")) print(app.config[""])
from flask import Flask
from config import config
app = Flask(__name__)
# 第一种
# app.config["SECRET_KEY"] = "hujinmg"
# 第二种
# app.config.from_object(config)
# 第三种
# app.config.from_pyfile("config2.py")
# 第四种
app.config.from_envvar("PROJECT",silent=True)
@app.route("/")
def index():
print(app.config.get("SECRET_KEY"))
return f"SECRET_KEY = {app.config.get('SECRET_KEY')}"
if __name__ == '__main__':
app.run(debug=True)
新版方式,可以不用写app.run(),使用flask run进行启动,需要传入环境变量用于指定是启动那个文件。
flask run == python.exe -m flask run
查看所有路由信息app.url_map
- 第一种
-
window
set FLASK_APP=hello.py -
linux
export FLASK_APP=hello.py -
windows设置环境变量
set 变量名=变量值 -
linux设置环境变量
export 环境变量=变量值
- 第二种
print(app.url_map)
Flask默认有三种请求
GET HEAD OPTIONS
405 方法不允许
@app.route("/1",methods=['post'])
def index():
return "111"
print(app.url_map)
如果只有methods=[‘Post’]的话
使用app.url_map可以产看到,只支持Options和Post,没有Get了。
Map([<Rule '/1' (POST, OPTIONS) -> index>,
<Rule '/static/<filename>' (OPTIONS, HEAD, GET) -> static>])
蓝图blueprint
使用蓝图可以创建一个蓝图Blueprint里面可以编写 名字,name
- 使用蓝图
- user = Blueprint(“user”,name) # 创建蓝图对象
- @user.route("/") # 使用创建好的对象进行添加路由
- 注册蓝图 app.register_blueprint(user,url_prefix="/路径") # 在app主启动类中,注册蓝图并添加访问url路径前缀
- 蓝图导入
一般将蓝图配置放到__init__中。需要注意的是要将文件导入进去。
解决办法是:在__init__初始化类中,导入views这个路由类。这样在from goods import goods_bp时会加载__init__类中的内容。__init__类中在去加载views类。
蓝图内部的静态文件,必须要显示指出。没有默认的。和Flask函数的参数一样
转换器converters
代码展示
from flask import Flask
# werkzeug => 工具
from werkzeug.routing import BaseConverter
app = Flask(__name__)
# 使用系统自带的int转换器。
@app.route("/user/<int:userid>")
def converter(userid):
print("userid type is {}".format(type(userid)))
return "userid is {}".format(userid)
# 自定义类,继承自BaseConver,重写regex属性。
class MyConverter(BaseConverter):
# regex正则。
# 1[3-9] =》 1开头 [3-9](3-9之间的数) \d 数字 \d{9}表示任意9个数
regex = r"1[3-9]\d{9}"
# url_map中的converters是个字典
app.url_map.converters["myPhoneNum"] = MyConverter
# 自定义converter,实现限制接口为/user/手机号(11位)
@app.route("/phone/<myPhoneNum:phoneNum>")
def myConverter(phoneNum):
print("type is {}".format(type(phoneNum)))
return "phone num is => {} ".format(phoneNum)
# 查看自带的转换器
print(app.url_map.converters.get("int")) # <class 'werkzeug.routing.IntegerConverter'>
print(app.url_map.converters.get("string")) # <class 'werkzeug.routing.UnicodeConverter'>
converter(转换器)文本描述。
flask作为轻型WEB框架,在web开发中,可能会出现限制用户访问的规则的场景,那么这个时候,就需要过滤指定用户,所以可以使用转换器实现。
转换器的本质是:通过正则表达式匹配路由地址
flask 系统自带的转换器有
DEFAULT_CONVERTERS = {
'default': => UnicodeConverter,
'string': => UnicodeConverter,
'any': => AnyConverter,
'path': => PathConverter,
'int': => IntegerConverter,
'float': => FloatConverter,
'uuid': => UUIDConverter,
}
转换器总结:
- 使用系统自带的转换器。
只用在动态的接口前加个 系统自带的转换器即可 如 int:url变量 - 自定义转换器
当系统自带的转换器不能满足需求时,我们就要自定义转换器。分为以下步骤
- 导入包 from werkzeug.routing import BaseConverter
- 自定义类继承自BaseConverter,重写regex属性
- 使用app.url_map.converters[key] = 自定义类
- 在动态url变量前 限制即可。 如@app.route("/phone/myPhoneNum:phoneNum")
abort
abort: 中止
想要中止程序并返回个错误信息,可以使用abort
abor(400) => 直接中止执行返回个错误(bad request) 到页面中
使用
https://blog.csdn.net/aimianwo1708/article/details/101975369
- 导入包:from flask import abort
- abort(400) or 其他错误代码
错误处理 errorhandle
本来如果报错的话前端页面会显示出英文错误。可以设置请求报错时的显示。
app.errorhandle(404)
代码
from flask import Flask
# werkzeug => 工具
from werkzeug.routing import BaseConverter
app = Flask(__name__)
@app.errorhandler(500)
def error500(e):
print(e)
return "500错误"
@app.errorhandler(404)
def error404(e):
print(e)
return "404页面走丢了"
@app.errorhandler(ZeroDivisionError)
def zeroDivisionError(e):
print(e)
return "除数不能为零"
@app.route("/zero")
def zero():
1 / 0
return "1/0"
@app.route("/user/<int:userid>")
def converter(userid):
print("userid type is {}".format(type(userid)))
return "userid is {}".format(userid)
class MyConverter(BaseConverter):
# regex正则。
# 1[3-9] =》 1开头 [3-9](3-9之间的数) \d 数字 \d{9}表示任意9个数
regex = r"1[3-9]\d{9}"
# url_map中的converters是个字典
app.url_map.converters["myPhoneNum"] = MyConverter
# 自定义converter,实现限制接口为/user/手机号(11位)
@app.route("/phone/<myPhoneNum:phoneNum>")
def myConverter(phoneNum):
print("type is {}".format(type(phoneNum)))
return "phone num is => {} ".format(phoneNum)
# 查看自带的转换器
print(app.url_map.converters.get("int")) # <class 'werkzeug.routing.IntegerConverter'>
print(app.url_map.converters.get("string")) # <class 'werkzeug.routing.UnicodeConverter'>
Flask-Script
Flask Script: Flask-Script的作用是可以通过命令行的形式来操作Flask。
例如通过命令跑一个开发版本的服务器、设置数据库,定时任务等。
- 安装
pip install flask_script
flask_script代码
day03 > init.py文件
from flask import Flask
def create_app():
app = Flask(__name__)
return app
根目录下manage.py
from flask_script import Manager
from day03 import create_app # 使用绝对路径,引入day03包__init__.py下的create_app函数
from day03.db_scripts import db_manager # 导入day03包下db_scripts类中的Manger()生成的变量
app = create_app() # 使用__init__下创建的flask实例
manager = Manager(app) # 创建命令行启动控制对象
@manager.command # 使用@变量名.command注册函数
def runserver():
print("服务器跑起来了。")
@manager.command
def hujing(args):
return "args is {}".format(args)
# 导入文件,起名为db , Manage变量
manager.add_command("db",db_manager)
if __name__ == '__main__':
manager.run() # 底层还是调用的app.run()
day03 > db_scripts.py
from flask_script import Manager
db_manager = Manager()
@db_manager.command
def init():
print("数据库初始化完成")
@db_manager.command
def migrate():
print("数据库迁移成功")
注意:如果Flask是2.x版本以上会报
Flask Error:ModuleNotFoundError: No module named ‘flask._compat‘错误
是因为,Flask 2.0及以上版本中将 _compat.py 文件删除了,导致在导包的时候报错。
解决办法:
点击报错信息,进入到__init__文件中,将 from flask._compat import text_type 语句改成 from flask_script._compat import text_type 即可,如下所示:(crtl+f查找)
# from flask._compat import text_type
from flask_script._compat import text_type
scripts运行,因为没有app.run()所以不能通过右击运行项目。要想使用要用命令行执行。
- 启动
(venv) C:\Users\Administrator\Desktop\flask-study>python manage.py runserver
服务器跑起来了。
(venv) C:\Users\Administrator\Desktop\flask-study>python manage.py db init
数据库初始化完成
(venv) C:\Users\Administrator\Desktop\flask-study>python manage.py db migrate
数据库迁移成功
Flask_script也可以开启Debug像app.run那样
from flask_script import Manager,Server
from day03 import create_app
from day03.db_scripts import db_manager
app = create_app()
manager = Manager(app)
@manager.command
def runserver():
print("服务器跑起来了。")
@manager.command
def hujing(args):
return "args is {}".format(args)
manager.add_command("db",Server(use_debugger=True))
@app.route("/")
def index():
return "111"
if __name__ == '__main__':
manager.run()
- 导入Server
- 在manager.add_command(“db”,Server(use_debugger=True))中添加Server(use_debugger=True)
- 在pycharm上添加参数(db --threaded)=》 db跟上面add_command第一个参数一样
循环引用
模型类
主启动类
产生的结果
这里报了,不能导入Article,但是导入命令是没有错误的。
错误的原因是,主启动类运行时,从上向下执行。在执行到第五行的时候去导入了modules.py文件
加载modules.py文件时,有from 主类 import db,这个db还没被执行,所以报错。
解决办法:
单独创建个文件,用来存放db,避免两个py文件,互相引用。
一般用extensions.py来存放。 extensions(扩展)
SQLAlchemy
SqlAlchemy介绍https://www.liaoxuefeng.com/wiki/1016959663602400/1017803857459008
SQLAlchemy使用
- 要创建数据库表中字段的关系映射
from flask_sqlalchemy import SQLAlchemy
from flask import Flask
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql+pymysql://root:123456@127.0.0.1/flask_demo"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app=app)
# print(db)
class Student(db.Model):
__tablename__ = "students"
id = db.Column(db.Integer,primary_key=True,comment="唯一标识")
sno = db.Column(db.String(10),unique=True,nullable=False,comment="学号")
name = db.Column(db.String(70),nullable=False,comment="姓名")
grade = db.Column(db.Enum("男","女"),nullable=False,comment="性别")
age = db.Column(db.Integer,nullable=False,comment="年龄")
id_card = db.Column(db.String(18),nullable=False,comment="身份证")
def __repr__(self):
return f"<Student> name:{self.name}"
class Teacher(db.Model):
__tablename__ = "teachers"
id = db.Column(db.Integer,primary_key=True)
name = db.Column(db.String(200),nullable=False)
age = db.Column(db.Integer)
# 新增字段(使用flask_migrate + flask_scirpt)
# id_card = db.Column(db.String(18),unique=True)
def __repr__(self):
return f"<Teacher name:{self.name},age:{self.age}>"
if __name__ == '__main__':
# db.drop_all() # 删除数据库表
db.create_all() # 通过上面的模型创建对应的数据库表。若创建在别的py中,要导入模型后在创建
t1 = Teacher(name="刘秋玲",age=50)
t2 = Teacher(name="杨明明",age=25)
db.session.add_all([t1,t2]) # 插入数据
db.session.commit() # insert update delete要commit提交事务
sql = "select * from teachers"
datas = db.session.execute(sql).fetchone() # 获取第一条数据,获取所有fetchall() or all()
print(datas)
- 增删改查
from Modules import db,Student,Teacher # 导入编写好的数据库表模型,和db属性
from sqlalchemy import create_engine # create_engine(url) 数据库url
from sqlalchemy.orm import sessionmaker # sessionmaker(bind) 绑定创建好的engine
db.drop_all()
db.create_all()
# 创建连接数据库的引擎,session连接数据库需要
# '数据库类型+数据库驱动名称://用户名:口令@机器地址:端口号/数据库名'
engine = create_engine("mysql+pymysql://root:123456@127.0.0.1/flask_demo")
# 绑定数据库,并生成Session对象
Session = sessionmaker(bind=engine)
# 实例化一个Session
session = Session()
# 增
s1 = Student(sno="19378",name="hj",grade="男",age=19,id_card="341302202207106522")
s2 = Student(sno="19379",name="张安",grade="女",age=19,id_card="341302202207106523")
s3 = Student(sno="19380",name="张安1",grade="女",age=19,id_card="341302202207106524")
s4 = Student(sno="19381",name="张安2",grade="女",age=19,id_card="341302202207106525")
db.session.add_all([s3,s4,s1,s2]) # add添加一个,add_all添加多个
db.session.commit()
# 查
# 查询所有
gs = Student.query.all()
for g in gs:
print(g.name)
# 查询第一条
g1 = Student.query.get(1)
print(g1.id,g1.sno,g1.name,g1.grade,g1.age,g1.id_card)
# 查询id不等于0的
gs = Student.query.filter(Student.id != 0).all()
print(gs) # [<Student> name:张安1, <Student> name:张安2, <Student> name:hj, <Student> name:张安]
# 有__repr__函数的话,打印repr函数的返回值,没有则[<Student 1>, <Student 2>, <Student 3>, <Student 4>]
for g1 in gs:
print(g1.id,g1.sno,g1.name,g1.grade,g1.age,g1.id_card)
# 查询name=张三的第一条数据
# g2 = Student.query.filter_by(name = "张三").first()
# print(g2.name,g2.age,g2.id_card)
# 查询id不等于0,并且对id进行降序排序
g4 = Student.query.filter(Student.id !=0).order_by(Student.id.desc())
for g in g4:
print(g.id,g.name,g.age,g.grade,g.id_card,g.sno)
# 使用session.execute(sql).all =》 可以使用原生sql语句进行查询
print(session.execute("select * from students order by id desc").all())
# 改
u1 = Student.query.filter(Student.grade == "男").update({"grade":"女"})
db.session.commit() # update insert delete语句要提交事务
session.execute("update students set grade = '男' where id = 2 ")
session.commit()
print("-----")
print(session.execute("select * from students").fetchall())
# 删除students表中的数据
db.session.execute("delete from students")
db.session.commit()
# 更换数据库
engine = create_engine("mysql+pymysql://root:123456@127.0.0.1/bilibili")
Session = sessionmaker(bind=engine)
session = Session()
print(session.execute("show tables").all()) # 查看有那些数据库表
print(session.execute("select * from watch limit 5").all())
初始化db有以下两种方式,第二种是若当前类中没有app可以先空着,在有db的py文件中在初始化
- db = SqlAlchemy(app)
- db = SqlAlchemy() => db.init_app(app)
Flask-Migrate(数据库迁移)
Flask-Migrate主要是用于,当数据库表字段改变时,更新数据库表。
Q:为什么要使用Flask-Migrate呢?
A:因为不使用Flask-Migrate的话,怎么更新字段!要使用db.create_all()创建表,但字段增加时该命令不会修改字段。要使用db.drop_all()删除掉表,在db.create_all()创建。字段一致了,但数据库里的数据没了。
为了满足 当数据库表字段发生改变时(新增或删除字段),数据库数据不丢失,所以使用Flask-Migrate。
- 要想使用Flask-Migrate需要安装
安装:使用 pip install flask_migrate==2.7.0 , 最好使用3.x以下版本
换个低版本的把 3.0.0这个版本MigrateCommand好像被去掉了
我一开始使用3.0.2版本时,没有MigrateCommand这个模块,回退回2.x版本可以正常使用
modules,在Teacher模型中,新增了字段 ‘id_card’ 字段
from flask_sqlalchemy import SQLAlchemy
from flask import Flask
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql+pymysql://root:123456@127.0.0.1/flask_demo"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app=app)
# print(db)
class Student(db.Model):
__tablename__ = "students"
id = db.Column(db.Integer,primary_key=True,comment="唯一标识")
sno = db.Column(db.String(10),unique=True,nullable=False,comment="学号")
name = db.Column(db.String(70),nullable=False,comment="姓名")
grade = db.Column(db.Enum("男","女"),nullable=False,comment="性别")
age = db.Column(db.Integer,nullable=False,comment="年龄")
id_card = db.Column(db.String(18),nullable=False,comment="身份证")
def __repr__(self):
return f"<Student> name:{self.name}"
class Teacher(db.Model):
__tablename__ = "teachers"
id = db.Column(db.Integer,primary_key=True)
name = db.Column(db.String(200),nullable=False)
age = db.Column(db.Integer)
# 新增字段
id_card = db.Column(db.String(18),unique=True)
def __repr__(self):
return f"<Teacher name:{self.name},age:{self.age}>"
if __name__ == '__main__':
# db.drop_all()
db.create_all()
t1 = Teacher(name="刘秋玲",age=50)
t2 = Teacher(name="杨明明",age=25)
db.session.add_all([t1,t2])
db.session.commit()
sql = "select * from teachers"
datas = db.session.execute(sql).fetchone()
print(datas)
使用,代码如下:
from flask_script import Manager, Server
from flask_migrate import Migrate,MigrateCommand
from study.Modules import Student,Teacher,db,app
# t1 = Teacher(name="lisi",age=19)
# print(t1)
# ts = db.session.execute("select * from teachers").fetchall() # 可以使用execute(sql)使用sql查询,感觉比用filter那种方便很多。
# for t in ts:
# print(t)
migrate = Migrate(app,db)
manager = Manager(app)
manager.add_command("db",MigrateCommand)
if __name__ == '__main__':
# 运行
manager.run()
使用上面,flask-script配置使用,用命令如下:
(venv) C:\Users\Administrator\Desktop\SqlAlchemy_study>python manage.py db init
Creating directory C:\Users\Administrator\Desktop\SqlAlchemy_study\migrations … done
Creating directory C:\Users\Administrator\Desktop\SqlAlchemy_study\migrations\versions … done
Generating C:\Users\Administrator\Desktop\SqlAlchemy_study\migrations\alembic.ini … done
Generating C:\Users\Administrator\Desktop\SqlAlchemy_study\migrations\env.py … done
Generating C:\Users\Administrator\Desktop\SqlAlchemy_study\migrations\README … done
Generating C:\Users\Administrator\Desktop\SqlAlchemy_study\migrations\script.py.mako … done
Please edit configuration/connection/logging settings in ‘C:\Users\Administrator\Desktop\SqlAlchemy_study\migrations\alembic.ini’ before proceeding.
该命令执行后,会在同级目录下生成一个文件夹migrations
(venv) C:\Users\Administrator\Desktop\SqlAlchemy_study>python manage.py db migrate
INFO [alembic.runtime.migration] Context impl MySQLImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.autogenerate.compare] Detected added column ‘teachers.id_card’
INFO [alembic.autogenerate.compare] Detected added unique constraint ‘None’ on ‘[‘id_card’]’
Generating C:\Users\Administrator\Desktop\SqlAlchemy_study\migrations\versions\31eb34397083_.py … done
可以实现数据库迁移仓库创建:
此时生成一个versions文件夹:
从数据库中能看到alembic_version表:
(venv) C:\Users\Administrator\Desktop\SqlAlchemy_study>python manage.py db upgrade
INFO [alembic.runtime.migration] Context impl MySQLImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.runtime.migration] Running upgrade -> 31eb34397083, empty message
更新字段到数据库表。
执行python manage.py db upgrade前
执行python manage.py db upgrade后
总结:
-
python manager.py db init:这个命令不需要执行,因为已经初始化了迁移脚本的环境,这个命令只执行一次。
-
python manager.py db migrate:这个命令需要执行,因为模型改变了。
-
python manager.py db upgrade这个命令也需要执行,每次运行了migrate命令后,就记得要运行这个命令。