flask知识点

falsk

# flask优点
flask: 是一个轻量级的框架, 只提供了基本的路由路径处理工具(Werkzeug), 和页面渲染功能(jinja2),如果要实现其他额外的功能需要安装扩展包.
​
里面提供了两个核心内容: Werkzeug + jinja2
Werkzeug: 负责处理请求相关内容
jinja2: 负责渲染页面的.
额外扩展: 比如邮件发送Flask_Mail,Flask_Sqlalchemy,等等.

知识点-1

01.Flask类创建对象的默认参数

# 参数:__name__,如果从当前模块启动,值是__main__,如果从其他模块调用运行,值是模块名
print(app.static_folder)  # folder 文件夹  app为application缩写
​
# 默认参数:static_folder, 默认值是static, 用来存储静态资源的,绝对路径,例如
/home/python/Desktop/Program/flask/static
​
# 默认参数:static_url_path, 默认值是/static,访问静态资源的路径
/static
​
# 默认参数:template_folder: 默认值是templates,用来存储模板文件
templates

02.url_map

# 格式: app.url_map
返回: 是一个map集合,里面装着地址(路径)和视图函数之间的映射关系,例如,
Map([<Rule '/static/<filename>' (GET, HEAD, OPTIONS) -> static>])

03.视图函数指定访问方式(methods)

# 格式: @app.route('/路径',methods=['访问方式1','访问方式2'])
methods设置请求的方式,默认就是GET请求,例如,
@app.route("/methods", methods=["GET", "POST", "PUT"])
常见的访问方式: GET,POST,PUT,DELETE,等8种

04.url_for

# url_for,是flask提供的一个方法,用来通过视图函数名称找到视图函数地址的方法
# 格式: url_for('视图函数名称',key=value)
注意:函数名,要加双引号 ""
@app.route("/urlfor1")
def url_for1():
    body_02 = url_for('url_for2')  # 根据函数名得到路径
    return "url_for1 <a href='%s'>点我</a>" % body_02
    
@app.route("/urlfor2")
def url_for2():
    return "url_for2 <a href='%s'>点我</a>" % url_for('url_for1')

05.redirect

# redirect,重定向,通过路由地址,找到对应的视图函数名称
格式: redirect('路由地址'), 返回的就是一个响应体对象
场景: 向服务器A请求资源1, 没有这个资源直接跳转到2
特点 : 两次请求
注意:当资源2路由路径里带参数时,需要利用url_for("函数名",参数=xx),得到其路由路径

06.jsonify

# 作用:前后端在交互的时候,格式一般指定成为json格式,因为这样的交互效率最高.
1.设置响应体对象的headers属性
resp = make_response('helloworld')
resp.headers['Content-Type'] = 'application/json'
return resp
​
2.使用flask中提供的方法,jsonify()
dict = {
"name":"zhangsan",
"age":"13"
}
return jsonify(dict)
​
简化写法:
return jsonify(name='zhangsan',age=13)
​
@app.route("/")
def hello_world():
    # 响应头里的Content-Type:application/json
    return jsonify(name="zs", age="18")
# 结果为:
{
  "age": "13",
  "name": "zhangsan"
}

07.参数类型

# 在访问视图函数的时候,如何指定参数类型,比如:整数,小数,字符串等内容
格式: @app.route('/地址/<参数类型:变量名>')
常见参数类型:
    int     整数
    float   小数
    path    字符串类型
接收字符串(path),默认可以省略

08.自定义类型

# 自定义类型(转换器)格式:
1.自定义类,继承自BaseConverter
2.编写初始化方法,init方法,接收两个参数,url_map,regex,并初始化父类空间和子类空间
3.将自定义转换器类,添加到默认的转换列表中
app.url_map.converters[key] = 转换器类
自定义转换器方法之to_python方法:
执行时间: 匹配到规则之后, 执行视图函数之前
作用: 用来过滤数据,还可以做编码转换
 
​
from flask import Flask
from werkzeug.routing import BaseConverter
​
app = Flask(__name__)
​
# 1.自定义,继承自BaseConverter
class MyRegexConverter(BaseConverter):
    # 不够灵活
    # regex = '\d{3}'
​
    # 2.编写初始方法,init方法,接收两个参数,url_map,regex,并初始化父类空间和子类空间
    def __init__(self, url_map, regex):
        super(MyRegexConverter, self).__init__(url_map)
        self.regex = regex
​
    # 执行时间:匹配到规则之后,执行视图函数之前
    # 作用:用来过滤数据,还可以做编码转换
    def to_python(self, value):
        print("value = %s" % value)
        return int(value) + 10  # 最终返回的结果
​
# 3.自定义转换器类,添加到默认的转换列表中
app.url_map.converters['re'] = MyRegexConverter  # 定义下面的匹配的名字
​
# 打印所有的规则
print(app.url_map.converters)
​
# re调用到了初始方法,传递了,app.url_map, 参数2:正则
@app.route('/<re("\d{3}"):num>')
def hello_world(num):
    print("num = %s" % num)
    # 如果没有to_python方法,默认返回该结果
    # 如果有了to_python方法,先把参数num返回给to_python的value,在把返回值返回给num
    return "the num is %s" % num
​
# 接收一个手机号
@app.route('/<re("1[3-9]\d{9}"):mobile>')
def get_phone_number(mobile):
    return "the mobile is %s" % mobile
​
if __name__ == '__main__':
    app.run(debug=True)

09.abort主动抛出异常

# abort(代号),主动抛出异常代号
场景: 当访问服务器资源的时候,如果不存在/权限不够,可以直接使用abort抛出异常
@app.errorhandler(代号/对象): 可以捕捉异常代号,返回统一的界面.装饰方法执行
    
结束进程
    当前端口号为5000时,
查看端口占用情况: lsof -i:5000
    杀掉进程: kill 进程编号(pid)
        
 
from flask import Flask
from flask import abort
​
app = Flask(__name__)
​
@app.route('/game/<int:age>')
def game(age):
    if age < 18:
        abort(404)
        # 1 / 0  # 当本身是异常情况时,也会跑出异常
        # raise Exception()  # 主动抛出异常
    return "play game"
​
@app.errorhandler(404)
def not_found_page(e):
    # 必须设定一个参数e,接受原来报错的消息
    print(e)  # 404 Not Found:...
    # 把原来的报错消息覆盖掉,返回该消息
    return "找不到页面了"
​
@app.errorhandler(Exception)
def error(e):
    return "报错了"
​
if __name__ == '__main__':
    app.run(debug=True)

10.debug里的参数

在使用app.run()的时候,可以传递参数
参数有: host: IP地址默认是127.0.0.1
参数有: port: Port端口默认是5000
参数有: debug: 默认是False
如果设置成True,会有很多好处
app.run(host='127.0.0.1', port=5001, debug=True)

11.app运行加载参数

app.run()运行的时候有些配置信息需要加载,有三种方式可以加载内容
方式一: 可以从类中加载
    app.config.from_object(类名称)
方式二: 可以从配置文件
    app.config.from_pyfile(文件名称)
方式三: 可以从环境变量加载,依赖文件,不常用
    app.config.from_envvar('环境名称key')

12.request请求

request,是Werkzeug提供好的请求对象,里面封装了请求相关的所有信息,比如:请求地址,请求参数,请求方式,等等
request.url: 请求地址
request.method: 请求方式
request.args: 请求参数,并且是问好后面拼接的内容,www.baidu.com?name=zhangsan&age=13
request.args为字典,取值用get来
​
request.data  非表单和ajax提交,接收到的格式为json,需要转换
json_data = request.data
dcit_data = json.loads(json_data)
​
request.form  去表单的数据,也是字典的格式
例如,print(request.args['name']) #获取不到报错
print(request.args.get('name'))  # 获取不到返回None

知识点-2

01.请求勾子

# @app.before_first_request
在处理第一个请求前执行,例如
@app.before_first_request
def before_first_request():
    print("before_first_request")
​
# before_request
在每次请求前执行
如果在某修饰的函数中返回了一个响应,视图函数将不再被调用
​
# after_request
如果没有抛出错误,在每次请求后执行
接受一个参数:视图函数作出的响应, response
在此函数中可以对响应值在返回之前做最后一步修改处理
需要将参数中的响应在此参数中进行返回, return 
​
# teardown_request
在每次请求后执行
接受一个参数:错误信息,如果有相关错误抛出
​
from flask import Flask, request
app = Flask(__name__)
​
# 在每次请求执行前,适合做数据库的链接,表的创建
@app.before_first_request
def before_first_request():
    print("before_first_request")
​
@app.before_request
def before_request():
    # 获取参数,结社进行解密
    token = request.args.get("token")  # 请求的参数,字典的格式,查询字符串,必须要带这个查询字符串才能访问
    # 如果解密出来不等于3.证明已经被改过了
    if token != "3":
        return "你是坏蛋,不能访问"  # 如果该代码执行了,下面代码部秩序
    print("before_request")
​
# 如果没有抛出错误(服务器内部异常),在每次请求后执行,适合对响应的数据统一处理,比如设置json格式
@app.after_request
def after_request(resp):  # 必须要有参数,来接受响应内容
    print("after_request")
    resp.headers["Content-Type"] = "application.json"
    return resp
​
# 在每次请求后执行,用来记录服务器内部异常的,将其写入到文件中
@app.teardown_request
def teardown_request(e):  # 必须要设置参数,接受服务器内部异常
    print(e)
    print("teardown_request")
​
@app.route('/')
def hello_world():
    print("hello world")
    return "hello world"
​
if __name__ == '__main__':
    app.run(debug=True)

02.响应内容response

# 格式一
a.直接返回, 响应体  
return "hello"
​
b.返回,响应体 + 状态码  
return "hello","666 BIGERROR"
BIGERROR对666进行的说明
​
c.返回, 响应体 + 状态码 + 响应头(字典格式)
return "hello", "666 BIGERROR",{"name":"banzhang","Content-Type":"application/json"}
​
# 格式二
创建响应体,设置内容再返回
resp = make_response('hello')  # 创建响应体
resp.status = '888 XIAGAO'  # 响应状态
resp.headers["content-Type"] = "application/json"  # 响应头
return resp

03.状态保持

状态保持,通过cookie和session来进行状态保持
# cookie
用来保存浏览器和服务器交互信息的内容,由服务器设置,存储在浏览器
设置方式:
resp.set_cookie(key,value,max_age)
max_age:表示cookie的有效时间
获取方式:
value = request.cookies.get(key)
注意点: 如果不设置cookie的有效期,默认就是一次浏览器会话结束,即关闭浏览器
​
​
from flask import Flask, make_response, request
app = Flask(__name__)
​
@app.route('/set_cookie/<path:content>')
def set_cookie(content):
    # 创建响应体
    resp = make_response("set_cookie")
    resp.set_cookie("name", content)
    # 13必须为字符串,过10秒了,该值就消息,及13会消失
    resp.set_cookie("age", '13', 10)  # 单位秒
    return resp
​
@app.route('/get_cookie')
def get_cookie():
    # 获取cookie,做对应的商品推荐
    # 请求的cookie里取值
    name = request.cookies.get("name")
    age = request.cookies.get("age")
    return "name is %s, age is %s" % (name, age)
    pass
​
if __name__ == '__main__':
    app.run(debug=True)
​
# session
也是用来做状态保持,比如:登陆状态, 适合存储敏感信息,比如,卡号,密码等,由服务器设置,存储在服务器,需要依赖于cookie(sessionID存储cookie)
设置session:
    session[key] = value
获取session:
    value = session.get(key)
注意点:
1.由于session空间的钥匙(sessionID),存储在浏览器cookie,所以需要加密存储
2.加密存储需要设置秘钥SECRET_KEY
​
​
from flask import Flask
from flask import session
​
app = Flask(__name__)
app.config['SECRET_KEY'] = 'DFSAFSDFSDF'
​
# 设置session
@app.route('/set_session/<path:name>')
def set_session(name):
    session["name"] = name
    return "set_session"
​
# 获取session
@app.route('/get_session')
def get_session():
    name = session.get("name")
    return "get_session,name is %s" % name
​
if __name__ == '__main__':
    app.run(debug=True)

04.上下文(g)

# 请求上下文
    request: 相当于一个容器,或者是一个线程局部变量.request是和每一个请求相关的.
    session: 相当于一个容器,存储在服务器内部的.线程局部变量.
​
# 应用上下文
    current_app: 相当于是app的一个代理对象,主要使用在模块开发中.
    g:  g.name 可以用在请求勾子里的 before_request,li
​
        
from flask import Flask, current_app, g
app = Flask(__name__)
​
@app.before_request
def before_request():
    g.name = "zhangsan"
​
@app.route('/')
def hello_world():
    print(current_app.url_map)
    name = g.name
    return "hello world ---> {}".format(name)
    pass
​
if __name__ == '__main__':
    print(app.url_map)
    app.run(debug=True)      

05.扩展包flask_script

作用: 
1.用来通过命令的方式去启动程序,并且可以指定端口和ip,
2.并且配合flask_migrate可以做数据库迁移.
​
使用流程:
1.安装,pip install flask_script
2.导入包中类
    from flask_scirpt import Manager
3.使用Manager管理app
manager = Manager(app)
4.启动程序
​
注意点:
1. 配置右键运行,右上角编辑configration添加runserver
2. 配置debug模式,在管理之前配置
3.运行程序, python xxx.py runserver -h ip地址  -p 端口
​
​
from flask import Flask
from flask_script import Manager
​
app = Flask(__name__)
​
class Config(object):
    DEBUG = True
​
app.config.from_object(Config)  # 通过类来加载配置
​
# 3.使用Manager管理app
manager = Manager(app)
​
@app.route('/')
def hello_world():
    return "hello world"
    pass
​
if __name__ == '__main__':
    manager.run()  # 里面没有debug参数

06.template模板

flask中提供了两个核心功能,werkzueg + jinja2
jinja2就是用来做模板(html文件)渲染,使用的是render_template('页面',key=value,key2=value2)渲染
取变量的值:
    {{ 变量名 }}
    
分支语句if:
    {% if 条件A %}
        语句1;
    {% elif 条件B %}
        语句2;
    {% else %}
        语句3;
    {% endif %}
    
循环结构for:
{% for 变量名  in  容器 %}
​
{% endfor %}
返回的值为:
return render_template("file01render_template.html")

07.filter过滤器

# 1.字符串过滤器
    safe:禁用转义, 让html标签生效
<p>{{ '<h1>hello</h1>' | safe }}</p>
    capitalize:把变量值的首字母转成大写,其余字母转小写, 中文没有大小写
    lower:把值转成小写
    upper:把值转成大写
    title:把值中的每个单词的首字母都转成大写
    reverse:字符串反转
    format:格式化输出,例如:
<p>{{ '%s is %d' | format('age',17) }}</p>
    striptags:渲染之前把值中所有的HTML标签都删掉,和safe相反,例如,
<p>{{ '<h1>hello</h1>' | striptags }}</p>  # 让h1标签失去效果结果为 hello
​
# 2.列表过滤器
    first:取第一个元素,例如,
<p>{{ [1,2,3,4,5,6] | first }}</p>
    last:取最后一个元素
    length:获取列表长度
    sum:列表求和,例如,
<p>{{ [1,2,3,4,5,6] | sum }}</p>
    sort:列表排序

08.自定义过滤器

# 自定义过滤器,当系统提供的过滤器满足不了需要的时候就可以自定义
​
# a.先定义函数,然后再将函数添加到默认的列表过滤器就可以
def test():
    pass
app.add_template_filter(函数名称,'过滤器名称')
​
例如:
def get_jishu(list1):
    new_list = []
    for item in list1:
        if item % 2 != 0:
            new_list.append(item)
    return new_list
app.add_template_filter(get_jishu, "jishu")
​
# b.定义函数的时候,直接使用列表装饰器装饰
@app.template_filter('过滤器名称')
def test()
    pass
例如:
@app.template_filter("oushu")
def get_oushu(list2):
    new_list1 = []
    for item in list2:
        if item % 2 == 0:
            new_list1.append(item)
    return new_list1
​
# 特殊变量
    loop.index0,编号0开始
    loop.index,编号1开始

09.模板复用

# a.宏的复用(macro)
宏(macro): 实际上相当于python中函数,提前定义好一段功能,需要的时候调用
定义格式:
{% macro 宏名(参数) %}
{% endmacro %}
例如:
{% macro test(name1,age1) %}
<label for="">{{ name1 }}</label>  # 参数要写在{{}}里面
<input type="text" value="123"><br>
<label for="">{{ age1 }}</label>
<input type="text" value="456">
{% endmacro %}
{{ test("姓名:","age:") }}  # 宏名的调用也要写到{{}}里面
​
在当前文件调用:
{{ 宏名(参数) }}
​
其他文件定义的,调用
{% import '宏文件名' as 别名 %}
{{ 别名.宏名(参数) }}
{% import "file06import.html" as test1 %}
{{ test1.test("姓名:","年龄:") }}
    
场景: 对于表单结构固定,但是内容不固定,可以使用
# b. 继承复用(extends)
继承: 共性抽取, 代码复用,并且可以扩展子类自己的内容,更加灵活
格式: {% extends '文件名' %}
父文件名定义,一般切割很多块:
{% block 模块名称 %}
{% endblock %}  # {{ super()}}的用法
# 父类
{% block title %}
<h1>全场大甩卖</h1>
{% endblock %}
{% block content %}
<h3>只要998</h3>
{% endblock %}
{% block bottom %}
<h4>thankyou</h4>
{% endblock %}
​
# 子类
{% extends "file08father.html" %}
{% block content %}
{# 包含父类的内容 #}
{{ super() }}
<h3>不要钱了</h3>
{% endblock %}
# c. 包含复用(incloud)
格式: {% include '文件名' %}
格式: {% include '文件名' ignore missing %} 如果文件名不存在不会报错
​
{% include "file07children.html"  ignore missing%}
该文件里的效果和file07里的效果一样

10.jinja2里特殊变量(flash)

jinja2中给提供了一些特殊的变量,不需要传递就可以使用
常用的有:
config: app的配置对象
request: 请求对象
url_for(): 方法,通过函数名称找路径
get_flashed_messages(): 消耗消息队列中的数据,也就是存储在也就是存储在flash里面的数据
​
存数据:flash("数据")
一次性把数据全部取出来:get_flashed_messages(),得到的是一个列表
​
注意点:
1,使用flash,存储数据的时候需要,设置SECRET_KEY
2.因为flash,依赖session
app.config["SECRET_KEY"] = "fjkdjfkdjfkdjfkdf"

知识点-3

01.传统表单

传统表单组成:
是由大量的html标签组成的,唯一繁琐的地方在于一旦表单写好之后,
如果要进行更改和增加验证功能比较麻烦.
案例: 注册案例
注意点:
如果是表单提交,使用request.form获取
拓展:all([参数1,参数2])表示如果里面有一个参数为空的话,就为False,例如,
if not all([username, password, repassword])
    
"""
传统表单组成:
是由大量的html标签组成的,唯一繁琐的地方在于一旦表单写好之后,
如果要进行更改和增加验证功能比较麻烦.
案例: 注册案例
​
注意点:
如果是表单提交,使用request.form获取
"""""
from flask import Flask, render_template, request
app = Flask(__name__)
​
@app.route('/')
def show_page():
    return render_template('file01normal_form.html')
​
@app.route('/register', methods=['POST'])
def register():
    # 获取参数,即获取表单的内容
    username = request.form.get('username')
    password = request.form.get('password')
    repassword = request.form.get('repassword')
​
    # 校验参数(为空校验,两次输入的密码是否一致)
    # all([参数1,参数2])表示如果里面有一个参数为空的话,就为False
    if not all([username, password, repassword]):
        return "参数不全,注册失败"
​
    # 两次输入的密码是否一致
    if password != repassword:
        return "两次密码不一致,注册失败"
    # 返回注册信息
    return "注册成功"
if __name__ == '__main__':
    app.run(debug=True)
 
    
# 前端的界面
{#action后面表示存储的地址,点击提交之后,以post的方式去访问该网址,
把该表单的内容发到/register,即页面跳到/register#}
<form action="/register" method="post">
{#    input里面的name属性在这里可以理解字典的key,value可以理解为字典的值vlaue#}
    <label>用户名:</label><input type="text" name="username"><br>
    <label>密码:</label><input type="password" name="password"><br>
    <label>确认密码:</label><input type="password" name="repassword"><br>
    <input type="submit" value="注册">
​
</form>
​
</body>
</html>

02.flask_wtf表单

# 介绍:属于flask的扩展包,通过类名称渲染表单,然后字段的形式渲染标签.
# 好处:
1.提供了验证函数
2.提供了csrf校验机制
​
1.安装扩展
    pip install flask_wtf
    from flask_wtf import FlaskForm
    from wtforms import 字段
    from wtforms.validates import 验证函数
    
2.自定义类继承自FlaskForm,编写字段和验证函数
​
3.创建表单,进行渲染
​
# 注意点:
1.由于flask_wtf表单提供了csrf验证机制,csrf_token的生成需要依赖于SECRET_KEY进行加密
2.如果是wtf表单,获取数据方式, form.username.data
3.flask_wtf表单默认开启了csrf校验.关闭方式: 
4.通过validate_on_submit(),会校验:'POST', 'PUT', 'PATCH', 'DELETE'请求方式校验, 还会校验验证函数和csrf_token

2.1.WTForms支持的HTML标准字段

字段对象 说明
StringField 文本字段
TextAreaField 多行文本字段
PasswordField 密码文本字段
HiddenField 隐藏文件字段
DateField 文本字段,值为 datetime.date 文本格式
DateTimeField 文本字段,值为 datetime.datetime 文本格式
IntegerField 文本字段,值为整数
DecimalField 文本字段,值为decimal.Decimal
FloatField 文本字段,值为浮点数
BooleanField 复选框,值为True 和 False
RadioField 一组单选框
SelectField 下拉列表
SelectMutipleField 下拉列表,可选择多个值
FileField 文件上传字段
SubmitField 表单提交按钮
FormField 把表单作为字段嵌入另一个表单
FieldList 一组指定类型的字段

2.2WTForms常用验证函数

验证函数 说明
DataRequired 确保字段中有数据
EqualTo 比较两个字段的值,常用于比较两次密码输入
Length 验证输入的字符串长度
NumberRange 验证输入的值在数字范围内
URL 验证URL
AnyOf 验证输入值在可选列表中
NoneOf 验证输入值不在可选列表中

使用 Flask-WTF 需要配置参数 SECRET_KEY。

CSRF_ENABLED是为了CSRF(跨站请求伪造)保护。 SECRET_KEY用来生成加密令牌,当CSRF激活的时候,该设置会根据设置的密匙生成加密令牌。

2.3flask_wtf的使用

"""
flask_wtf表单,属于flask的扩展包,通过类名称渲染表单,然后字段的形式渲染标签.
好处:
1.提供了验证函数
2.提供了csrf校验机制
    
1.安装扩展
    pip install flask_wtf
    from flask_wtf import FlaskForm
    from wtforms import 字段
    from wtforms.validates import 验证函数
​
2.自定义类继承自FlaskForm,编写字段和验证函数
3.创建表单,进行渲染
​
"""""
from flask import Flask, render_template
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, EqualTo
​
app = Flask(__name__)
​
# 设置密码
app.config['SECRET_KEY'] = "fjdkfjkdkjfkd"
# 关闭csrf校验
# app.config["WTF_CSRF_ENABLED"] = False
​
# 自定义类继承自FlaskForm,编写字段和验证函数
class MyForm(FlaskForm):
    # 编写字段
    username = StringField(label='用户名', validators=[DataRequired('用户不能为空')])
    password = PasswordField(label='密码', validators=[DataRequired('密码不能为空')])
    repassword = PasswordField(label='确认密码', validators=[DataRequired('确认密码不能为空'),
                                                         EqualTo('password', '两次密码不一致')])
    submit = SubmitField('提交')
​
# 页面展示
@app.route('/')
def hello_world():
    # 创建表单, 进行渲染
    form = MyForm()
​
    return render_template('file02wtf_form.html', form=form)
​
# 注册内容处理
@app.route('/register', methods=['POST'])
def register():
    # 创建表单(根据提交的内容创建的表单)
    form = MyForm()
    # 校验表单,如果满足校验函数,返回true,否则返回false
    if form.validate_on_submit():  #在提交后,对表单的数据进行校验
        # 获取参数内容
        print(form.username.data)  # 获取表单的数据
        print(form.password.data)
        print(form.repassword.data)
        return "注册成功"
    # 返回注册信息
    return "注册失败"
​
if __name__ == '__main__':
    app.run(debug=True)
​
​
# 前端部分
<form action="/register" method="post">
​
    {#    设置隐藏字段csrf_token#}
    {{ form.csrf_token }}
​
    {{ form.username.label }}
    {{ form.username }}<br>
​
    {{ form.password.label }}
    {{ form.password }}<br>
​
    {{ form.repassword.label }}
    {{ form.repassword }}<br>
​
    {{ form.submit }}
​
</form>

03.ORM模型

ORM模型类表示的是扩展包flask_sqlahchemy中的SQLALchemy类
orm: 将模型类中的类名,属性,对象,映射成数据库里面的表名,字段,一行一行数据.
    
# 使用操作流程:
1.安装,导入包
    pip install flask_sqlalchemy
    pip install flask_mysqldb # 驱动包,那么协议名mysql
    pip install pymysql #驱动包, 那么协议名 mysql+pymysql
    from flask_sqlalchemy import SQLAlchemy
​
2.设置配置信息,比如:账号,密码地址等信息
3.创建SQLAlchemy类对象,关联app
4.编写模型类,继承db.Model
5.操作数据库使用方法而不是sql语句了.
    添加单个对象:db.session.add(obj)
    添加多个对象:db.session.add_all([obj1,obj2])
    提交数据库: db.session.commit()
    创建表: db.create_all(),创建继承db.Model的表
    删除所有表: db.drop_all()  删除继承db.Model的表
    删除: db.session.delete(obj)
    回滚: db.session.rollback()   
 
# 注意点:
1. 如果安装的数据库驱动是flask_mysqldb 格式:
        mysql://<用户名>:<密码>@<IP地址>:<端口>/<数据库名>
2. 如果安装的数据库驱动是pymysql 格式
        mysql+pymysql://<用户名>:<密码>@<IP地址>:<端口>/<数据库名>
3.默认生成的表名称是类名称小写,如果需要手动指定表名,编写__tablename__ = '表名'
使用orm的优点
不需要写sql语句,提高开发效率
缺点:
由于不是通过sql语句直接操作数据库,所以有性能损失.

3.1数据库的操作

# 属性设置在单方 
# 外键设置在多方
​
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
​
# 配置信息
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql+pymysql://root:[email protected]:3306/demo"
# 设置压制警告信息,如果True会追踪数据库变化,会增加显著开销,所以建议设置为False
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
​
# 创建SQLAlchemy类对象,关联app
db = SQLAlchemy(app)
​
# 编写模型类,继承db.Model
# 角色,用户之间的关系
class Role(db.Model):
    __tablename__ = "roles"  # 指定表名称
    # 参数1:表示整数类型,  参数2:表示主键
    id = db.Column(db.Integer, primary_key=True)
    # 角色名唯一的
    name = db.Column(db.String(64), unique=True)
​
    # 需要设置关系属性relationship(不会产生字段),设置在一方
    # 给Role添加了user关系属性,查询格式:role(Role对象).users(关系名),知道了该角色对应了那些用户
    # 给User添加了role关系属性(反向引用).查询格式:user(User对象).role(关系名)
    # 设置lazy='dynamic',保证通过关系得出来的数据结构为[对象1,对象2]
    users = db.relationship('User', backhref='role',lazy='dynamic)
​
    # 为了方便的看到对象输出的内容__repr__, 如果是普通类__str__
    def __repr__(self):
        return "<Role:%s>" % self.name
​
# 用户(多方),外键设置在多方
class User(db.Model):
    __tablename__ = "users"  # 指定表名称
    # 参数1:表示整数类型,  参数2:表示主键
    id = db.Column(db.Integer, primary_key=True)
    # 用户名唯一的
    name = db.Column(db.String(64), unique=True)
​
    # 外键
    role_id = db.Column(db.Integer, db.ForeignKey(Role.id))  # 类名.id
​
    # 为了方便的看到对象输出的内容__repr__, 如果是普通类__str__
    def __repr__(self):
        return "<User:%s,%s>" % (self.id, self.name)
​
@app.route('/')
def hello_world():
    return "helloworld"
​
if __name__ == '__main__':
    # 为了演示方便,先删除数据库表,和模型类关联的表
    db.drop_all()
    # 创建表,所有继承自dbModel的表
    db.create_all()
    app.run(debug=True)

3.2常用的SQLAlchemy字段类型

db.Column(db.类型,)

类型名 python中类型 说明
Integer int 普通整数,一般是32位
SmallInteger int 取值范围小的整数,一般是16位
BigInteger int或long 不限制精度的整数
Float float 浮点数
Numeric decimal.Decimal 普通整数,一般是32位
String str 变长字符串
Text str 变长字符串,对较长或不限长度的字符串做了优化
Unicode unicode 变长Unicode字符串
UnicodeText unicode 变长Unicode字符串,对较长或不限长度的字符串做了优化
Boolean bool 布尔值
Date datetime.date 时间
Time datetime.datetime 日期和时间
LargeBinary str 二进制文件

3.3常用的SQLAlchemy列选项

选项名 说明
primary_key 如果为True,代表表的主键
unique 如果为True,代表这列不允许出现重复的值
index 如果为True,为这列创建索引,提高查询效率
nullable 如果为True,允许有空值,如果为False,不允许有空值
default 为这列定义默认值

3.4常用的SQLAlchemy关系选项

db.relationship('类名',backref='关系名')

选项名 说明
backref 在关系的另一模型中添加反向引用
primary join 明确指定两个模型之间使用的联结条件
uselist 如果为False,不使用列表,而使用标量值
order_by 指定关系中记录的排序方式
secondary 指定多对多关系中关系表的名字
secondary join 在SQLAlchemy中无法自行决定时,指定多对多关系中的二级联结条件

04.数据库的查询操作

# 类名.query.过滤器或者方法
User.query.all() 查看所有的结果
User.query.get(6),6为id
User.query.filter_by(id=4).all(),得出的结果为列表
User.query.filter(User.id==4).first(),里面是条件,得出来是单个对象
User.query.filter(User.name.endswith('g')).all()
User.query.filter_by(id=4).first(),该过滤器用的很少
过滤器一般后面要有方法
​
# 非
User.query.filter(User.name != "wang").all()
# 与
User.query.filter(User.name.endswith("li"),User.name.startswith("li")).all(),条件1和条件2中间加一个,即 条件1,条件2
# 或
from sqlalchemy import or_  # 要先导入
User.query.filter(or_(User.password=="123456",User.email.endswith('itheima.com'))).all(),即 or_(条件1,条件2)
​
# 在...里  in_   in_([])
User.query.filter(User.id.in_([1,3,5,7,9])).all()
​
# contains
User.query.filter(User.name.contains('g')).all()
​
# count()
 User.query.count()
​
# order_by
User.query.order_by(User.email.desc()).all()
默认为asc()升序,desc()降序
​
# paginate
paginate = User.query.paginate(2,3,False),2表示当前的第2页,3表示每页显示3个,设置为False,没找到也不会报错
例如,有10条数据
paginate.pages ,表示总共几页,4页
paginate.page,  当前页,2
paginate.itmes, 当前页的数据
​
# 删除指定的数据
user1 = User.query.get(1)
db.session.delete(user1)    

4.1常用的SQLAlchemy查询过滤器

过滤器 说明
filter() 把过滤器添加到原查询上,返回一个新查询
filter_by() 把等值过滤器添加到原查询上,返回一个新查询
limit 使用指定的值限定原查询返回的结果
offset() 偏移原查询返回的结果,返回一个新查询
order_by() 根据指定条件对原查询结果进行排序,返回一个新查询
group_by() 根据指定条件对原查询结果进行分组,返回一个新查询

4.2常用的SQLAlchemy查询执行器

方法 说明
all() 以列表形式返回查询的所有结果
first() 返回查询的第一个结果,如果未查到,返回None
first_or_404() 返回查询的第一个结果,如果未查到,返回404
get() 返回指定主键对应的行,如不存在,返回None
get_or_404() 返回指定主键对应的行,如不存在,返回404
count() 返回查询结果的数量
paginate() 返回一个Paginate对象,它包含指定范围内的结果

知识点-04

01.多对多

注意点:(学生课程表)
    1.需要在关系属性中添加secondary = 中间表名
    2.外键写在中间表中
    3.关系属性的编写依赖于外键,如果没有外键,关系属性不能使用
    4.lazy:可以设置为dynamic, 可以动态查询(用到的时候查询),默认是子查询(subquery)

02.数据库迁移

是通过扩展包,flask_migrate和flask_script来实现
操作流程:
1.安装,导入
    pip install flask_migrate  提供两个类,Migrate, MigrateCommand
    pip install flask_script,  提供了Manager
​
2.创建Manager对象,关联app
3.使用Migrate类,关联app和db
4.添加一条操作命令MigrateCommand
​
5.编写操作语句
    1.生成迁移文件(一次就好)
    python xxx.py db init
    
    2.生成迁移脚本(记录模型类的字段信息)
    python xxx.py db migrate -m"版本名(注释)"
    
    3.更新迁移脚本到数据库(upgrade调用的是迁移脚本中的方法)
    python xxx.py db upgrade/downgrade [version]
    
    4.查看所有版本号
    python xxx.py db history
    
    5.查看最新版本号
    python xxx.py db show
    
注意点:
降级有风险,操作需谨慎,(数据丢失)
​
​
​
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
​
app = Flask(__name__)
# 配置数据库信息
# 配置信息
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql+pymysql://root:[email protected]:3306/demo"
# 设置压制警告信息,如果True会追踪数据库变化,会增加显著开销,所以建议设置为False
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
​
# 创建模型类
class Student(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64))
    age = db.Column(db.Integer)
    
# 创建SQLAlchemy对象
db = SQLAlchemy(app)
​
# 2.创建Manager对象,关联app
manager = Manager(app)
# 3.使用Migrate类,关联app
Migrate(app, db)
# 4.添加一条操作命令MigrateCommand
manager.add_command('db', MigrateCommand)
​
@app.route('/')
def hello_world():
    return "hello world"
​
if __name__ == '__main__':
    manager.run()

03.蓝图

# 蓝图格式
在蓝图包的init里写,1,导入蓝图,2创建蓝图对象
在views里写,3.蓝图对象装饰可视函数
在一个模块名a里写,4.蓝图注册到app中,导入蓝图包
​
# 蓝图的使用
蓝图: 是flask中专门用来模块化开发的技术,不需要安装扩展包就可以使用
使用流程:
1. 导入包
    from flask import Blueprint    
2. 创建蓝图对象
3. 使用蓝图对象,装饰视图函数
# 4 单独为一个文件,用app.register_blueprint(蓝图对象),然后启动该文件就好
4. 将蓝图注册到app中 
​
# 创建蓝图对象,里面的参数说明
# 参数1: cart指定的是蓝图的名称,在输出url_map的时候,用来标记视图函数属于哪个模块
# 参数2: __name__表示当前蓝图所在的模块名称,cart
# 参数3:url_prefix 视图函数的访问名称,所有使用cart_blue所装饰的视图函数访问的时候都要加上前缀
# 参数4:static_folder是用来存储静态资源的
# 参数5:template_folder, 模板文件
cart_blu = Blueprint("cart", __name__, url_prefix='/cart', static_folder='static', template_folder='templates')
​
​
# __init__.py文件
from flask import Blueprint
# 创建蓝图对象
cart_blue = Blueprint('cart', __name__)
# 导入所有该蓝图对象装饰的可视函数
from cart import views
​
# views.py文件
# 导入蓝图对象
from cart import cart_blue
# 导入cart包时,会自动调用下面的__init__模块
# 蓝图对象装饰可视函数
@cart_blue.route('/goods')
def goods():
    return 'goods'
​
@cart_blue.route('/list')
def lists():
    return 'list'
​
​
# 01_flask.py
from flask import Flask
from cart import cart_blue
app = Flask(__name__)
​
# 注册蓝图对象
app.register_blueprint(cart_blue)
​
if __name__ == '__main__':
    app.run()

04.断言assert

assertEqual     如果两个值相等,则pass
assertNotEqual  如果两个值不相等,则pass
assertTrue      判断bool值为True,则pass
assertFalse     判断bool值为False,则pass
assertIsNone    不存在,则pass
assertIsNotNone 存在,则pass
self.assertIsNotNone(author2,'对象不能为空')
​
正常的 assert 布尔表达式,说明
在测试案例中,即在类中抵用
self.assertEqual(布尔表达式,说明)
​
​
断言(assert): 一般就用在单元测试,和框架中.
格式:
    assert boolean表达式1, 语句A
执行流程:
    如果表达式1,为True,继续向后执行
    如果表达式1,为False,抛出异常,执行语句A
    
def div(num1, num2):
    assert isinstance(num1, int), 'num1必须是整数'
    assert isinstance(num2, int), 'num2必须是整数'
​
    return num1 / num2

猜你喜欢

转载自blog.csdn.net/weixin_42149982/article/details/82908179