flask日常梳理
2019-03-16 本文已影响0人
battleMonkey
1.日常flask报错(没secret_key)
# 解决: 未设置 SECRET_KEY
SECRET_KEY = 'asdfewfasdfeasdfe'
2.导入 import 技巧
from . import xxx # . 表示从当前位置 导入
3.python3.6技巧
# type:声明一个None对象的类型
redis_store = None # type:StrictRedis
# 这样可以使用 该 类型的 方法(pycharm可以有提示)
4.git提交空文件夹:gitkeep
在 要提交的 空文件夹 新建 .gitkeep文件 实现 提交 空文件夹 到 码云
5.session 以 redis 存储 配置 app 参数的一些探究
SESSION_PERMANENT = False/True # 指定 session 在 浏览器 存储方式(默认值为True)
如果 TRUE
-浏览器 session 生存时间 受 PERMANENT_SESSION_LIFETIME = timedelta(days=1) 影响
如果 False
-浏览器 session 生存时间 为 关闭浏览器
无论 SESSION_PERMANENT 如何变化
redis存储session 生存时间 只受 PERMANENT_SESSION_LIFETIME = timedelta(days=1) 影响
6.session存储:
收到客户端的session创建请求, 则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个session id将被在本次响应中返回给客户端保存。
7.返回网站图标的 视图函数的 三种返回方式
from flask import render_template, redirect, current_app, send_file
...
@index_blu.route("/favicon.ico")
def favicon():
# redirect("/static/news/favicon.ico")
# 返回图片
# return current_app.send_static_file("news/favicon.ico")
return send_file("/static/news/favicon.ico")
8. import . # 导入当前目录包下的 初始化文件 .init
9.json相关
-json_data = request.data
-json_dict = json.loads(json_data)
# 两步,封装合为一步,即:
- dict_data = request.json
...
-jsonify(key1 = value1, key2 = value2 ...)
10.视图函数 的url地址,必须以"/"开头
# 抛出ValueError('url必须以斜杠开头')
raise ValueError('urls must start with a leading slash')
# ValueError: url必须以斜杠开头
ValueError: urls must start with a leading slash
即: 创建的蓝图对象 前缀 没有加 "/"
11.pycharm 快捷键
ctrl + d 复制 改行内容 到下一行
12.禁止 浏览器 缓存 js,方便实时调试修改js代码
13.验证码请求技巧:
为了防止 图片请求的uuid 多次 在 服务器端 存储,可以在 前端 做 设置
前端js:
// TODO 生成一个图片验证码的编号,并设置页面中图片验证码img标签的src属性**
var cur_id = "" pre_id = ""**
function generateImageCode() {**
//1.生成一个随机字符串**
imageCodeId = generateUUID();**
//2.拼接图片url地址**
image_url = '/passport/image_code?cur_id='+imageCodeId + "&pre_id="+preimageCodeId**
//3.将地址设置到image标签的src属性中,为image_url**
$('.get_pic_code').attr('src',image_url)**
//4.记录上一次的编号**
preimageCodeId = imageCodeId**
//============================================================
//第一次前端变化:
<img src="/passport/image_code ?
cur_id=2b82b7ab-044e-4a89-b87c-62a508163fcb
&
pre_id=""
//(pre_id="" 为空,因为 pre_id = preimageCodeId,此时 preimageCodeId 未赋值)
class="get_pic_code" onclick="generateImageCode()">
//第二次前端变化:
<img src="/passport/image_code ?
cur_id=5cdfc91e-9e05-43b9-9f08-93d2b3902b29
&
//此处等于上一次的值
pre_id=2b82b7ab-044e-4a89-b87c-62a508163fcb "
class="get_pic_code" onclick="generateImageCode()">
后端:
@passport_blu.route('/image_code')
def get_image_code():
cur_id = request.args.get('cur_id')
pre_id = request.args.get('pre_id')
# print("image_code:%s"%cur_id)
# print("image_code:%s"%pre_id) # 第一次时 pre_id 为 空
if not cur_id:
abort(404)
# 调用generate_captcha 获取 图片验证码编号 验证码值 图片(二进制)
_, text, image_data = captcha.generate_captcha()
try:
redis_store.setex("image_code:%s"%cur_id, IMAGE_CODE_REDIS_EXPIRES, text)
if pre_id:
redis_store.delete("image_code:%s"%pre_id)
except Exception as e:
current_app.logger.error(e)
abort(500)
# response = make_response(image)
# response.headers['Content-Type'] = 'image/jpg'
return image_data
14.调用generate_captcha获取图片验证码编号验证码值图片(二进制)
_, text, image_data = captcha.generate_captcha()
# 解包成三个参数 name text值 image_data 二进制图片数据
15.在 Flask 中,可以很方便的返回自定义状态码,实现不符合 http 协议的状态码
例如:status code: 666
16.startUML -> 一款数据库模型类 软件
17.pycharm的 git 更改密码 windows - 打开 设置 搜索 ->凭据,删除密码
18.flask session 默认保存 31天
19. app(host = 0.0.0.0,) 暂时 只有 这里 可以 用 0.0.0.0 表示绑定当前 所有 ip
20.不推荐使用 json.dumps 转成 JSON 字符串直接返回
因为返回的数据要符合 HTTP 协议规范,如果是 JSON 需要指定 content-type:application/json
21.redis_store.delete(key)
如果 key 不存在, 不会报错 ,只会 返回 0 即:受影响 行数 为 0
22.raise 和 aborn 区别???
23.获取当前时间:
from datetime import datetime
datetime.now()
24.logger
current_app.logger.debug()
# 不是 loggin
25. 定义登陆装饰器, 封装用户的登陆数据时, 使用 数据库 的 User 模型类时,需注意避免产生循环导包问题
# 装饰器进阶(登陆状态封装):
# 定义登陆装饰器, 封装用户的登陆数据:
def user_login_data(view_func):
@wraps(view_func) # 需要导包 from functools import wraps
def wrapper(*args, **kwargs):
# 1. 从session中 取出用户的user_id
user_id = session.get("user_id")
# 2 通过user_id 取出 user 对象:
user = None
if user_id:
try:
# *** 防止 循环导包
from info.models import User
user = User.query.get(user_id)
except Exception as e:
current_app.logger.error(e)
# 3 将user数据 封装到g对象:
g.user = user # 封装flask的user 到 g 对象
return view_func(*args, **kwargs)
return wrapper
26.form 表单:
如果方法是 post,后端 需要 request.form
如果方法是 get , 后端 使用 request.args 就行
原理:
- 前端 GET表单数据提交 -> 不走 请求体
- get:表单数据会被 以参数的形式:name1=value1&name2=value2
附带在url?后面,再发送给服务器,并在url中显示出来。
27.cookie 最大 客户端 存储 单个4kb ,网站同源策略 最多12个
28. flask的路由 转换器(默认 六种, 写了七个 是因为 default = string)
系统自带转换器
DEFAULT_CONVERTERS = {
'default': UnicodeConverter,
'string': UnicodeConverter,
'any': AnyConverter,
'path': PathConverter,
'int': IntegerConverter,
'float': FloatConverter,
'uuid': UUIDConverter,
}
转换器 | 作用 |
---|---|
default (string) | 默认选项,接受除了斜杠之外的字符串 |
int | 接受整数 |
float | 接受浮点数 |
path | 和string类似,不过可以接受带斜杠的字符串 |
any | 匹配任何一种转换器 |
uuid | 接受UUID字符串 |
其中, path 允许匹配的路径 包含 '/' 适合接收 所有前端发来的 "url路径"
其余, 等同于 string
29.自定义参数类型(自定义转换器)
"""
- 背景:
- 如果系统提供的int,float,等参数类型满足不了需求的时候,我们需要自定义
- 之所以,int,float,path可以接收不同的数据类型,是因为,系统已经提供好对应的转换器了
- 自定义转换器格式
- 1.定义类,继承自BaseConverter
- 2.重写init方法
- 3.初始化父类成员变量, 还有子类自己的规则
- 4.将转换器类,添加到系统默认的转换器列表中
需求: 接收三位整数
"""
from flask import Flask
from werkzeug.routing import BaseConverter
app = Flask(__name__)
# - 1.定义类,继承自BaseConverter
class MyRegexConverter(BaseConverter):
#这样直接指定规则,不够灵活,具体应该匹配什么规则应该交给路由
# regex = "\d{3}"
# - 2.重写init方法,接收两个参数
def __init__(self,map,regex):
# - 3.初始化父类成员变量, 还有子类自己的规则
super(MyRegexConverter, self).__init__(map)
self.regex = regex
# - 4.将转换器类,添加到系统默认的转换器列表中
app.url_map.converters["re"] = MyRegexConverter
#打印输出所有的系统转换器列表
print(app.url_map.converters)
# =========================================================
#使用re('规则'),实际上是传递了两个参数,参数1: app.url_map, 参数2:括号中写的正则规则
# / < re( r "\d{3}" ) : number > r: 正则用法,忽略"转意字符"
# 转换器 re (self,map,regex)
#匹配三位整数
@app.route('/<re("\d{3}"):number>')
def hello_world(number):
return "the three number is %s"%number
#匹配四位整数
@app.route('/<re("\d{4}"):number>')
def get_four_number(number):
return "the four number is %s"%number
#匹配一个手机号
@app.route('/<re("1[3-9]\d{9}"):phone>')
def get_phone_number(phone):
return "the phone number is %s"%phone
if __name__ == '__main__':
app.run(debug=True)
30. 静态文件
动态 web 应用也会需要静态文件,通常是 CSS 和 JavaScript 文件。理想状况下, 你已经配置好 Web 服务器来提供静态文件,但是在开发中,Flask 也可以做到。 只要在你的包中或是模块的所在目录中创建一个名为 static 的文件夹,在应用中使用 /static 即可访问。
31.flask ORM 生成器relationship 可以支持 append 添加 数据?!
32. flask_ORM 使用小技巧
try:
filter = []
if cid != 1:
filter.append(News.category_id == cid)
# -如果cid = 1 -> filter = []
# filter: 解包 [] 为 空
# -如果cid != 1 -> filter = [ (News.category_id == cid), ]
# filter = [ (News.category_id == cid), ] 解包为 " News.category_id == cid "
# 这种方法,可灵活的在 filter = [] 添加其他过滤参数
# 1.查询 News.query 查询集 添加过滤条件 得出的过滤后查询集结果
paginate= News.query.filter(*filter)\ # paginate 在这里定义赋值
# 2.以 News对象的创建时间 降序排列
.order_by(News.create_time.desc())\
# 3.排列后的结果 进行分页 最后 得到的 是一个 分页对象!!!
.paginate(page,per_page,False)
except Exception as e:
current_app.logger.error(e)
return jsonify(errno=RET.DBERR ,errmsg="获取新闻列表失败")
# 分页对象的 使用:
totalPage = paginate.pages # 分页后 得到的总页数 # paginate 在这里被使用
currentPage = paginate.page # 分页后 目前的 页数
itemsObj_list = paginate.items # 当前页的所有 对象集合 # paginate 的items 记得 加 "s" 啊
33. 使用request时,最好要 确定 前端 传入的 数据 是什么 形式
$.ajax({
url:'/passport/login',
type:'post',
data:JSON.stringify(params),
contentType:'application/json', //传入的 数据 是 json
headers:{'X-CSRFToken':getCookie('csrf_token')},
success: function (resp) {
//判断是否登陆成功
if(resp.errno == '0'){
window.location.reload()
}else{
alert(resp.errmsg);
}
}
})
...
$.get("/newsList", params, function (resp) {...} //没有 注明,可能为 args 传参
34. app.init 配置 csrf_token(添加请求钩子)
from flask_wtf.csrf import generate_csrf
...
# 使用请求钩子 拦截所有的请求, 通过在中cookie设置csrf_token
@app.after_request
def after_request(resp):
resp.set_cookie("csrf_token", generate_csrf())
return resp
# 使用CSRFProtect保护app
CSRFProtect(app)
35. 首页新闻页试图函数 思路
1>为什么 需要定义新的视图函数?
因为需要让 首页 /index局部刷新
不可能说 获取了 很多 新闻之后, 一刷新 都没了
所以需要 定义新的 视图函数
2>一个不错的info模板(推荐添加到pycharm快捷键) -> setting 搜索 live 可以找到 快捷键设置
# 功能 :首页新闻列表的获取
###########################
# -请求路径 :/newsList
# -请求方式 :GET
# -携带参数 :cid, page, per_page
# -返回值 :jsonify
############################
3>前端传回cid的作用: 可以区分不同的新闻分类导航栏, 去查询数据库:
# -cid = 1 ,表示 最新, 不能 给 新闻 设置 cid = 1 ,
# 而是 应该 查询 所有新闻 并根据 "创建时间" 倒序排列,才是 最新新闻分类
# -page 和 per_page作用:
# 为了 给 后端 的 查询函数 paginate(page, per_page, False)
# 传递参数含义:
# - page: 当前页数
# - per_page: 每页 显示条数
# - False: 参数错误 不报错
# 传回参数:
totalPage = paginate.pages # 查到的 数据 总页数
currentPage = paginate.page # 当前 传回数据 的页数(即前端发送request 请求的页数)
itemsObj_list = paginate.items # 当前页数的 对象 列表