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