Flask框架学习笔记


pip list 查看安装了哪些库
pip freeze > 文件名.txt 将已经安装的第三方库导入到本地文件(项目路径中)
pip install -r 文件名 循环遍历导出的依赖库

标准的flask目录

  • static:下面存放静态资源
  • templates: 用于存放要选渲染的资源,一般为html (路径在flask程序的同级目录中) (render_template)
    在这里插入图片描述

flask的helloworld快速上手
在这里插入图片描述
分析:

  1. 首先导入依赖
  2. 初始化flask返回给变量app
  3. 使用@app.route设置路由(编写函数,解析路由)
  4. 启动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

指定加载配置数据

  1. 从配置对象加载 from_object
  2. 从本地配置类中加载 from_pyfile
  3. 从环境变量加载 from_envvar
  4. 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

  1. 第一种
  • window
    set FLASK_APP=hello.py

  • linux
    export FLASK_APP=hello.py

  • windows设置环境变量
    set 变量名=变量值

  • linux设置环境变量
    export 环境变量=变量值
    在这里插入图片描述

  1. 第二种
    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

  • 使用蓝图
  1. user = Blueprint(“user”,name) # 创建蓝图对象
  2. @user.route("/") # 使用创建好的对象进行添加路由
  3. 注册蓝图 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变量
  • 自定义转换器
    当系统自带的转换器不能满足需求时,我们就要自定义转换器。分为以下步骤
  1. 导入包 from werkzeug.routing import BaseConverter
  2. 自定义类继承自BaseConverter,重写regex属性
  3. 使用app.url_map.converters[key] = 自定义类
  4. 在动态url变量前 限制即可。 如@app.route("/phone/myPhoneNum:phoneNum")

abort

abort: 中止
想要中止程序并返回个错误信息,可以使用abort
abor(400) => 直接中止执行返回个错误(bad request) 到页面中

使用

https://blog.csdn.net/aimianwo1708/article/details/101975369

  1. 导入包:from flask import abort
  2. 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()
  1. 导入Server
  2. 在manager.add_command(“db”,Server(use_debugger=True))中添加Server(use_debugger=True)
  3. 在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使用

  1. 要创建数据库表中字段的关系映射
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)
  1. 增删改查
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命令后,就记得要运行这个命令。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值