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 = make_response(render_template("index.html"))
resp.headers['Content-Type'] = 'application/json'
return resp

2.使用flask中提供的方法,jsonify()
dict1 = {
"name":"zhangsan",
"age":"13"
}
return jsonify(dict1)

简化写法:
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.json 等于上面的两句话,得到的是字典格式的数据

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 = make_response("hello world")
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参数

# 调用manager.run()方法初始化Mnager实例来接收命令行输入。
python manage.py runserver

# 指定ip和端口启动
python manage.py runserver -h 127.0.0.1 -p 8090

python manage.py runserver --help  # 查看命令  -d debug
python manage.py runserver -h 127.0.0.1 -p 8080 -d --threaded
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()  # 添加对象时,要用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:mysql@127.0.0.1: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中类型说明
Integerint普通整数,一般是32位
SmallIntegerint取值范围小的整数,一般是16位
BigIntegerint或long不限制精度的整数
Floatfloat浮点数
Numericdecimal.Decimal普通整数,一般是32位
Stringstr变长字符串
Textstr变长字符串,对较长或不限长度的字符串做了优化
Unicodeunicode变长Unicode字符串
UnicodeTextunicode变长Unicode字符串,对较长或不限长度的字符串做了优化
Booleanbool布尔值
Datedatetime.date时间
Timedatetime.datetime日期和时间
LargeBinarystr二进制文件
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.过滤器或者方法,要加first()或all()
User.query.all() 查看所有的结果
User.query.get(6)6id
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对象,它包含指定范围内的结果
4.3 原生sql语句
@hello_blu.route("/db")
def get_db():
    # sql语句里的占位符格式::aid
    sql = """SELECT * FROM account
            WHERE id = :aid;"""
    # 以字典的格式把参数传递给sql
    ret = db.session.execute(sql, {"aid": 1})  # type: ResultProxy
    ret = ret.fetchall()
    # 遍历数据
    for item in ret:
        print(item)
    return "db"

知识点-04

01.多对多
注意点:(学生课程表)
	1.需要在关系属性中添加secondary = 中间表名
	2.外键写在中间表中
	3.关系属性的编写依赖于外键,如果没有外键,关系属性不能使用
	4.lazy:可以设置为dynamic, 可以动态查询(用到的时候查询),默认是子查询(subquery)
02.数据库迁移
# 注意:需要把模型类导入到manager.py里
是通过扩展包,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 manage.py db init
    
    2.生成迁移脚本(记录模型类的字段信息)
    python manage.py db migrate -m"版本名(注释)"
    
    3.更新迁移脚本到数据库(upgrade调用的是迁移脚本中的方法)
    python manage.py db upgrade/downgrade [version]
    
    4.查看所有版本号
    python manage.py db history
    
    5.查看最新版本号
    python manage.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:mysql@127.0.0.1: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
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值