FLASK框架
简介:
flask是一个非常小的python web框架 只提供了一个强劲的核心 其它都是通过第三方扩展库来实现
安装:
sudo pip install flask
虚拟环境
安装虚拟环境 pip install virtualenv
创建自己的虚拟环境 virtualenv –no-site-packages 虚拟环境的名称
启动虚拟环境 source venv/bin/activate
退出虚拟环境 deactivate (退出以后 进入到的是全局的·python环境)
虚拟环境迁移
pip freeze
pip list
pip freeze > requirment.txt
pip freeze
pip list
pip install -r requirment.txt
解释:虚拟环境 就是相当于自己新装了一个python的解释器 不受全局解释器 中的包的影响(独立) 如果在虚拟环境中 安装了flask,在退出虚拟环境以后 使用不了 flask的 因为你的全局的python解释器 中没有安装flask
一、完整的启动
from flask import Flask #导入Flask
app = Flask(__name__) #__name__是必传参数 flask类的实例化
#路由地址
#127.0.0.1:5000/
@app.route('/') #装饰器路由
def index(): #视图函数名称
return 'hello flask'
#只在当前主文件中启动 flask
if __name__ == '__main__':
app.run() #运行flask
二、app.run() 启动参数
参数 | 参数说明 |
---|---|
debug | 调试模式 默认为False 需要启动改为True |
port | 端口号 默认5000 |
host | 主机地址 默认127.0.0.1 ~ 127.255.255.254 |
threaded | 线程 默认False True开启多线程 |
实例:
#只在当前主文件中启动 flask
if __name__ == '__main__':
app.run(host='127.0.0.1',port=5001,debug=True,threaded=True) #运行flask
app.run(debug=True) #开启调试模式
三、视图函数
(1) 无参路由
@app.route('/')
def index():
return '首页'
(2) 带参路由
#访问地址 127.0.0.1:5000/welcome/zhangsan/
@app.route('/welcome/<name>/')
def welcome(name):
# return '必须有响应'
return '欢迎'+name
(3) 传递多个参数
#带多个参数
#http://127.0.0.1:5000/args/zhansan/18/
# @app.route('/args/<name>/<age>/')
# http://127.0.0.1:5000/args/zhansan_18/
@app.route('/args/<name>_<age>/')
def args(name,age):
return '我叫{} 我今年{}岁了'.format(name,age)
(4) 限制参数的类型
@app.route('/demo/<int:age>/') #限制传参类型为int
@app.route('/demo/<float:age>/') #限制传参类型为float
@app.route('/demo/<string:age>/') #默认就是字符串
@app.route('/demo/<path:age>/') #把参数age位置的参数中的/不再认为是路由地址的分隔符,而是参数的一部分 属于字符串类型的一部分
#/demo/zhangsan/a/b/c/
@app.route('/demo/<name>/<path:age>/') #把参数age位置的参数中的/不再认为是路由地址的分隔符,而是参数的一部分 属于字符串类型的一部分
# http://127.0.0.1:5000/demo/zhangsan/a/b/c/
@app.route('/demo/<path:age>/<name>/') #把参数age位置的参数中的/不再认为是路由地址的分隔符,而是参数的一部分 属于字符串类型的一部分
def demo(age,name):
print(age,name)
# print(type(age))
return '我是测试的视图'
注意:
- 每个路由地址结尾的/ 建议都加上 如果路由地址存在结尾的/ 浏览器会自动加上 如果没有结尾的 / 请求时 存在/ 则请求失败
- 传参的格式 <参数名称>
- 传递多个参数 使用/分隔符 或者_进行多个参数的拼接
- 参数传递进来都为字符串类型(默认)
- 限制参数类型 int/float/string/path
四、重定向
从一个视图跳转到另外一个视图的操作
from flask import redirect,url_for
(1) redirect 通过路由地址跳转
实例
@app.route('/')
def index():
return 'index.html'
@app.route('/args/<name>/<age>/')
def args(name,age):
return name+age
@app.route('/redirect/')
def myRedirect():
return redirect('/') #重定向到首页
return redirect(/args/zhansgan/18/)
(2) url_for 通过视图函数名 反向解析出路由地址
实例
@app.route('/')
def index():
return 'index.html'
@app.route('/args/<name>/<age>/')
def args(name,age):
return name+age
@app.route('/redirect/')
def myRedirect():
print(url_for('index')) # /
print(url_for('indexx')) # 报错 当前视图函数不存在
print(url_for('args',name='zhangsan',age=18) #/args/zhangsan/18/
return redirect(url_for('index')) #重定向到首页
return redirect(url_for('args'name='zhangsan',age=18))
注意:
当url_for 构造不存在的视图函数时 则报错
当url_for 构造需要传参的路由地址的时候 不存参则报错
五、abort(http标准状态码) 终止
from flask import abort
实例
@app.route('/')
def index():
print('abort上方')
# abort(404)
abort(500)
print('abort下方')
# return 'index.html'
参数e接受错误的信息
#捕获404状态码的错误信息
@app.errorhandler(404)
def page_not_found(e):
print(e)
# return 'page_not_found'
return e
#捕获500状态码的错误信息
@app.errorhandler(500)
def server_error(e):
return 'server_error'
注意:
abort上面的代码正常执行 下面的代码不再执行 和raise一样
六、响应 response
概述: 我们响应 可以给浏览器响应文字内容,html页面的代码 和 状态码 创建cookie
(1) 使用return 文字内容
@app.route('/')
def index():
return 'index.html'
(2) 响应内容并指定响应的状态码
@app.route('/')
def index():
return 'index.html',404
(3) 通过make_response 构造响应
from flask imoprt make_response
@app.route('/make_response/')
def makeResponse():
# res = make_response('make_response响应的内容')
res = make_response('make_response响应的内容',404)
return res
七、cookie 会话控制
协议:http
http:超文本传输协议 无状态协议
(1) 设置cookie 不设置过期时间
注意: 默认存活时间为 浏览会话结束(关闭浏览器)
主体结构
Response.set_cookie(
key,
value,
max_age=None #设置过期时间 单位为秒数
expires =None, #设置过期时间 为时间戳的秒数
path = ‘/’ #设置当前cookie存储为全局
)
实例
#cookie的操作
@app.route('/set_cookie/')
def set_cookie():
res = make_response('设置cookie')
res.set_cookie('name','zhangsan')
return res
(2) 获取cookie
@app.route('/get_cookie/')
def get_cookie():
# print(request.cookies['name']) #获取请求过来的cookie
# print(request.cookies.get('name')) #获取请求过来的cookie
return 'cookie中name的值为{}'.format(request.cookies.get('name','没有值'))
(3) 删除cookie
@app.route('/del_cookie/')
def del_cookie():
res = make_response('删除name的cookie')
res.delete_cookie('name')
return res
(4) 设置cookie 并设置过期时间
#cookie的操作
@app.route('/set_cookie/')
def set_cookie():
res = make_response('设置cookie')
# res.set_cookie('name','zhangsan') #不舍值过期时间
# res.set_cookie('name','zhangsan',max_age=60) #max_age 存活的秒数
lift_time = time.time()+40 #设置40秒以后的时间戳
res.set_cookie('name','zhangsan',expires=lift_time) #expires 值为存活的时间戳
return res
八、session
概述:服务器要识别 不同的用户的请求 根据cookie携带着的唯一session_id 值 来进行区分不同用户的请求
所以session基于cookie 如果cookie被禁用了 session也不能使用
session加密需要加密种子 secret_key 进行加密
(1) 设置session
注意: 不设置过期时间 默认为浏览会话结束
@app.route('/set_session/')
def index():
session['username'] = '张三'
return '设置set_session'
(2) 获取session
@app.route('/get_session/')
def get_session():
return '获取session 值为{}'.format(session.get('username'))
(3) 删除session
@app.route('/del_session/')
def del_session():
session.pop('username') #删除key为username的数据
# session.clear() #删除全部
return '删除session'
(4) 设置session及过期时间
@app.route('/set_session/')
def index():
session.permanent = True #持久化存储
app.permanent_session_lifetime = timedelta(minutes=5) #设置存活时间为5分钟
session['username'] = '张三'
return '设置set_session'
day2
一、request对象
浏览器带着用户的请求 经由flask框架创建出 request对象 request对象 包含当前用户的所有的请求信息
导入
from flask import request
request对象属性
url 完整的url请求
base_url 去掉get传参的url
host_url 只有主机地址和端口号
path 装饰中的路由地址
method 请求方法
args 获取get传参
request.args.get(key) 只能获取唯一key的值 如果key存在重复 则获取不到其它值
request.args.getlist(key) 获取key以列表形式返回(拿到所有的value)
form 获取表单post传递过来的数据
files 获取文件上传的数据
headers 获取请求头信息
cookies 获取所有的cookie
json 获取json数据
二、flask-script扩展
概念: 就是一个flask终端运行的解析器 因为在项目完成以后 就不应该有任何的改动 所有通过终端 添加不同的参数 来进行不同的启动项
安装
sudo pip3 install flask-script
使用
from flask_script import Manager
app = Flask(__name__)
manager= Manager(app)
...
if __name__ == '__main__':
manager.run()
启动命令
python3 文件名称.py runserver -d -r -h127.0.0.1 -p5000 –threaded
python3 manage.py runserevr -d -r #开启debug和自动加载
三、蓝本 blueprint
概念: 将视图拆分到不同的文件中 蓝本就是解决这个问题的
导入
from flask import Blueprint
user.py
from flask import Blueprint,request #导入蓝本
user = Blueprint('user',__name__)
@user.route('/login/')
def login():
return '登陆'
manage.py
from user import user #导入user蓝本对象
#注册蓝本
app.register_blueprint(user) #127.0.0.1:5000/login/
#设置当前蓝本的前缀
# app.register_blueprint(user,url_prefix='/user') #127.0.0.1:5000/user/login/
蓝本中的重定向
index->login
@app.route('/')
def index():
# return redirect(url_for('login')) #错误写法 报错
return redirect(url_for('user.login')) #user蓝本中的login视图函数
四、请求钩子函数
类Django框架的中间件
在主程序中使用
钩子函数 | 函数说明 |
---|---|
before_first_request | 第一次请求之前 |
before_request | 每次请求之前 |
after_request | 每次请求之后 |
teardown_request | 每次请求之后 即使有异常出现 |
实例
#第一次请求之前
@app.before_first_request
def before_first_request():
print('before_first_request')
#每次请求之前
@app.before_request
def before_request():
if request.path == '/form/' and request.method == 'GET':
abort(404)
print('before_request')
#每次请求之后
@app.after_request
def after_request(res):
print('after_request')
return res
#每次请求之后
@app.teardown_request
def teardown_request(res):
print('teardown_request')
return res
在蓝本中使用
钩子函数 | 函数说明 |
---|---|
before_app_first_request | 第一次请求之前 |
before_app_request | 每次请求之前 |
after_app_request | 每次请求之后 |
teardown_app_request | 每次请求之后 即使有异常出现 |
实例
#在蓝本中
#第一次请求之前
@user.before_app_first_request
def before_first_request():
print('before_first_request')
#每次请求之前
@user.before_app_request
def before_request():
if request.path == '/form/' and request.method == 'GET':
abort(500)
print('before_request')
#每次请求之后
@user.after_app_request
def after_request(res):
print('after_request')
return res
#每次请求之后
@user.teardown_app_request
def teardown_request(res):
print('teardown_request')
return res
模板引擎
模板引擎: 就是一定规则 书写 和 替换 最终展示成完整的html文件 响应给用户
模板引擎: jinja2
目录结构
project/
templates/ 模板目录
manage.py
(1) flask中渲染模板的函数
导入
from flask import render_template,render_template_string
render_template() #将模板渲染后响应给用户
@app.route('/')
def index():
# return render_template('index.html')
print(render_template('index.html')) #render_templates将模板代码渲染以后 响应给用户
return 'index.html'
render_template_string() #渲染字符串 响应给用户
@app.route('/')
def index():
return render_template_string('世界上最牛的字是哪一个?<span style="font-size:16px;color:red;">昊</span>')
(2) 模板中 分为俩大类
变量
{{ 变量名称 }}
注意:
如果在模板中存在变量 且视图函数没有传递 则插入空白字符(什么都没有) 不报错
实例
@app.route('/') def index(): return render_template('index.html',title='首页') <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{{ title }}</title> </head> <body> <h1>首页</h1> </body> </html>
标签
{% 标签名称 %}
(3) 过滤器
根据管道符 | 对传递的值进行过滤
abs 绝对值
val|abs
default 默认值
默认情况 只有给定的变量不存在则执行 默认值
{{ var|default(‘默认值’) }}
{{ False|default(‘默认值’) }} 不走
{{ 不存在的变量|default(‘默认值’) }} 执行默认值
设置default执行flase的默认值
{{ var|default(‘默认值’,boolean=True) }}
first 返回序列中的第一个
last 返回序列中的最后一个值
format 格式化字符串的输出
<li>{{ "我叫%s 我今年%d岁了 存款为%.2f元"|format('张三',20,1234) }}</li>
length 长度
join 拼接字符串
safe 不转义代码
int 转换为整形
float 转换为浮点形
list 转换为列表
lower 转换小写
upper 转换为大写
replace 替换
trim 去除俩测空白字符
striptags 去除html标签
使用的安装
sudo pip3 install 模块名称
- flask
- flask-script
- flask-moment
- flask-cache
- flask-uploads
- flask-wtf
- flask-bootstrap
- flask-sqlalchemy
- pillow
- pymysql
- flask-mail
- flask-migrate
(4) 标签
格式{% tag %}
if 标签
if…elif..else
{% if 90<=data.grade<=100 %}
<p>优</p>
{% elif 80<=data.grade %}
<p>良</p>
{% elif 70<=data.grade %}
<p>中</p>
{% elif 60<=data.grade %}
<p>合格</p>
{% else %}
<p>不及格</p>
{% endif %}
for 标签
{% for i in … %}
循环体
{% else %}
…
{% endfor %}
<ol>
{# {% for i in range(10) %}#}
{% for i in xx %}
{% for k,v in data.items() %} #迭代字典
<li>{{ i }}</li>
{% else %}
<li>你是否能看到我</li>
{% endfor %}
</ol>
注意:
当迭代的变量 不存在 则执行else
获取迭代的状态
变量 | 描述 |
---|---|
loop.index | 当前迭代的索引 从1开始 |
loop.index0 | 当前迭代的索引 从0开始 |
loop.first | 是否为第一次迭代 |
loop.last | 是否为最后一次迭代 |
loop.length | 迭代的长度 |
(5) 文件的包含 include
格式
{% include “路径/文件.html” %}
把文件引入到当前的位置 实现模板代码的复用
实例
header.html
<header>
头部
</header>
footer.html
<footer>
尾部
</footer>
test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{#{% include 'index.html' %}#}
{% include 'common/header.html' %}
<div>content内容部分</div>
{% include 'common/footer.html' %}
</body>
</html>
注意:
include 会将导入的代码 相当于粘贴到了 你导入的位置上 导入文件的所有代码都会在导入的位置显示出来
(6) 宏 macro
概述
宏相当于 python中的函数 需要定义 调用 传参
主体结构
{% macro 宏名称([参数...]) %}
内容
{% endmacro %}
调用
{{ 宏的名称([参数...]) }}
导入
在common下创建pubulicform.html文件
在form.html中导入
实例
from … import …
{% from 'common/publicform.html' import form %}
{{ form('用户名','text','username') }}
{{ form('密码','password','userpass') }}
{{ form('','submit','','提交') }}
import …
{% import 'common/publicform.html' as publicform %}
{{ publicform.form('用户名','text','username') }}
{{ publicform.form('密码','password','userpass') }}
{{ publicform.form('','submit','','提交') }}
注意
- 宏的调用只能在宏的下方来调用
- 宏如果有形参 且没有默认值 则可以不传实参
- 宏如果没有形参 则不可以传实参
- 宏的形参默认值 和python一样
- 宏的默认值 遵循默认值规则 有默认值的放在右侧 没有默认值的 放在左侧
- 关键字的参数使用 同python函数一样
day3
一、模板的继承 extends
概念: flask中的模板继承 可以通过继承 将很多重复的元素 抽离出来 放在父模板中 通过模板中 的extends,block 实现父模板继承和替换的使用
语法
{% extends ‘父模板的名称.html’ %}
{% block 替换的名称 %} 要替换的内容部分 {% endblock %}
自定义一个base.html模板
<!DOCTYPE html>
<html lang="en">
{% block header %}
<head>
{% block meta %}
<meta charset="UTF-8">
{% endblock %}
<title>{% block title %}base{% endblock %}</title>
<style>
{% block headercss %}
div{
font-size: 18px;
color: aqua;
}
{% endblock %}
</style>
{% block linkscript %}
{% endblock %}
</head>
{% endblock %}
<body>
<header>
头部
</header>
<div id="content">
{% block content %}
主体内容部分
{% endblock %}
</div>
<footer>
底部
</footer>
</body>
</html>
子模板 index.html
{% extends 'common/base.html' %}
{% block title %}
首页
{% endblock %}
{% block headercss %}
{{ super() }}
ul,li{
list-style:none;
margin:0;
padding:0;
}
li{
width:100px;
height:100px;
border-radius:50px;
background-color:yellow;
}
#content{
color:red;
}
{% endblock %}
{% block content %}
<h1>首页内容</h1>
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
{% endblock %}
{# 以下代码不会被加载 #}
{#{% block newblock %}#}
{# <h1>首页内容</h1>#}
{#{% endblock %}#}
{#sdasjdjsaldjaslkdalksjlksajdlasjdalskdjalkdsaskl#}
注意:
不可以在base模板block 以外的位置 添加任何的代码 因为都不会被加载
{{ super() }} 将base.html模板中替换的代码在调用(复用)
二、flask-bootstrap flask的bootstrap第三方扩展库
安装:
sudo pip3 install flask-bootstrap
使用
自定义base.html
{% extends 'bootstrap/base.html' %}
{% block title %}
boot-base 更改自己的base
{% endblock %}
{% block navbar %}
<nav class="navbar navbar-inverse" style="border-radius:0px;">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#"><span class="glyphicon glyphicon-eye-open"
aria-hidden="true"></span></a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="active"><a href="#">首页<span class="sr-only">(current)</span></a></li>
<li><a href="#">发表帖子</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
<li><a href="#">登录</a></li>
<li><a href="#">注册</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">个人中心 <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</a></li>
</ul>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
{% endblock %}
{% block content %}
<div class="container">
{% block pagecontent %}
<p>少小离家老大回</p>
<p>安能辩我是雌雄</p>
{% endblock %}
</div>
{% endblock %}
三、错误页面定制
manage.py
#错误页面定制
@app.errorhandler(404)
def page_not_found(e):
return render_template('error.html',code=404,e=e,info='您访问的页面被外星人抓走了')
@app.errorhandler(500)
def page_not_found(e):
return render_template('error.html',code=500,e=e,info='我们内部有些事情需要解决 请稍后访问')
error.html
{% extends 'common/boot-base.html' %}
{% block title %}
{{ code }}页面
{% endblock %}
{% block pagecontent %}
<h3>{{ info }}</h3>
<div class="alert alert-warning" role="alert"><h4>{{ code }}</h4>{{ e }}</div>
{% endblock %}
四、静态资源
创建static静态资源目录
目录结构
project/
templates/
common/
main/
index.html
....
static/
img/
css/
js/
upload/
manage.py
实例
{% block pagecontent %}
<h3>{{ info }}</h3>
<div class="alert alert-warning" role="alert"><h4>{{ code }}</h4>{{ e }}</div>
<img src="{{ url_for('static',filename='img/timg.jpg',_external=True) }}" alt="">
{% endblock %}
五、视图传递多个参数
(1) 原始传参
@app.route('/')
def index():
return render_template('index.html',arg1=v1,arg2=v2...)
(2) 使用字典
@app.route('/')
def index():
return render_template('index.html',data={'arg':'v1','arg2':'v2'...})
(3) 使用**
@app.route('/')
def index():
data={'arg':'v1','arg2':'v2'...}
return render_template('index.html',**data)
(4) locals()
以字典的形式获取局部变量
@app.route('/')
def index():
return render_template('index.html',**locals())变量
表单
六、flask原生表单
form1.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>原生表单</title>
</head>
<body>
<form action="{{ url_for('check_form',_external=True) }}" method="post">
<p>用户名 <input type="text" name="username"></p>
<p>密码 <input type="password" name="userpass"></p>
<p><input type="submit" value="submit"></p>
</form>
</body>
</html>
manage.py
#跳转到登录的表单页
@app.route('/login/')
def login():
return render_template('form1.html')
#表单提交过来处理的视图函数
@app.route('/check_form/',methods=['POST'])
def check_form():
print(request.form.get('username'))
print(request.form.get('userpass'))
return '提交过来了'
俩个合并为同一个
#表单提交过来处理的视图函数
@app.route('/login/',methods=['GET','POST'])
def check_form():
if request.method == 'POST':
print(request.form.get('username'))
print(request.form.get('userpass'))
return render_template('form1.html')
七、flask-wtf
概述: 是一个用于处理flask表单的扩展库 提供了表单校验 csrf等功能
安装
sudo pip3 install flask-wtf
csrf_token 的生成 需要依赖flask的secret_key
python中常量:一般用于配置文件的设置 变量的大写 在代码运行过程中 不能被修改和销毁的量 称之为常量(python中没有)
(1) 字段属性
字段类型 | 字段说明 |
---|---|
StringField | 普通文本字段 |
SubmitField | 提交按钮 |
PasswordField | 密码框 |
HiddenField | 隐藏域 |
TextAreaField | 多行文本域 |
DateField | 日期 |
DateTimeFIeld | 时间和日期 |
IntegerField | 整数 |
FloatField | 浮点数 |
BooleanField | 布尔字段 |
RadioFIeld | 单选框 |
SelectField | 下拉 |
FileField | 文件上传 |
(2) 验证器
验证器 | 验证器说明 |
---|---|
DataRequired | 必填 |
邮箱 | |
IPAddress | ip地址 |
Length | 长度 |
NumberRang | 值的范围 |
EqualTo | 验证俩个字段的值是否相同 |
URL | 验证url地址 |
Regexp | 正则验证 |
实例
form2.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
{# 添加csrf——token验证 #}
<p>{{ form.csrf_token }}</p>
<p>{{ form.username.label() }}</p>
<p>{{ form.username() }}
{# 显示错误信息的判断 #}
{% if form.username.errors %}
{{ form.username.errors.0 }}
{% endif %}
</p>
<p>{{ form.userpass.label() }}</p>
<p>{{ form.userpass() }}
{# 显示错误信息的判断 #}
{% if form.userpass.errors %}
{{ form.userpass.errors.0 }}
{% endif %}
</p>
<p>{{ form.submit() }}</p>
</form>
</body>
</html>
manage.py
from flask import Flask,render_template,request
from flask_script import Manager
from flask_bootstrap import Bootstrap
from flask_wtf import FlaskForm # 导入表单类的基类
from wtforms import StringField,PasswordField,SubmitField #导入字段类型
from wtforms.validators import DataRequired,Length #导入验证器
app = Flask(__name__)
# app.secret_key = 'abcd'
app.config['SECRET_KEY'] = 'abcd'
app.config['BOOTSTRAP_SERVE_LOCAL'] = True #加载本地样式
bootstrap = Bootstrap(app)
manager = Manager(app)
#自定义表单类
class Login(FlaskForm):
#username 为 name=username 用户名label标签和内容 validators验证器
username = StringField('用户名',validators=[DataRequired(message='用户名不能为空')])
userpass = PasswordField('密码',validators=[DataRequired(message='密码不能为空'),Length(min=6,max=12,message='密码长度为6~12位')])
submit = SubmitField('登录')
@app.route('/')
def index():
return render_template('index.html')
@app.route('/login/',methods=['GET','POST'])
def login():
form = Login() #实例化自定义表单类
if form.validate_on_submit(): #如果csrf_token和数据验证都通过则为真
return '数据提交过来了'
return render_template('form2.html',form=form)
if __name__ == '__main__':
manager.run()
day5 MODEL
flask作为一款MVT框架 也有自己的ORM flask-sqlalchemy
安装: sudo pip3 install flask-sqlalchemy
使用ORM原因:
当项目越来越大的时候 如果采用原生SQL的方式 就会有如下问题
- 出现大量的原生SQL,如果条件越多 代码越长 并且重复使用率低
- 很多的sql语句 都是在业务逻辑中拼接出来的 如果数据库需要更改 就要去修改这些sql的逻辑 很容易漏掉某些sql的修改
- 写原生sql容易忽略安全问题
使用ORM好处:
- 易用性 使用ORM做数据库的开发 可以有效的减少重复SQL的概率 写出来的代码 也更加的清晰直观
- 设计灵活 可以轻松的写出sql语句
- 可移植性
一、原生sql
(1) 创建一个库
create database if not exists bj1805 character set utf8;
(2) 创建表
create table if not exists user(
id int primary key auto_increment,
username varchar(20) not null default ‘xxx’,
sex tinyint default 1
)
(3) 导入 sqlalchemy
from sqlalchemy import create_engine
(4) 配置 URI 链接地址
DB_URI = ‘mysql+pymysql:// 用户名:密码@主机名:端口号/数据库名称’
实例
from sqlalchemy import create_engine
HOSTNAME = '127.0.0.1'
USERNAME = 'root'
PASSWORD = '123456'
PORT = 3306
DATABASE = 'bj1805'
DB_URI = 'mysql+pymysql://{}:{}@{}:{}/{}'.format(USERNAME,PASSWORD,HOSTNAME,PORT,DATABASE)
engine = create_engine(DB_URI)
#链接数据库
with engine.connect() as con:
#创建表
con.execute('create table test(username varchar(20))')
con.execute('insert into test values("xxxx")')
二、flask-sqlalchemy的使用
(1) 导入
from flask_sqlalchemy import SQLalchemy
(2) 配置
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:123456@127.0.0.1:3306/bj1805'
#是否追踪数据发生的改变 占用额外资源 将值改为True或者Flase进行禁用
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
(3) 实例化
db = SQLalchemy(app)
三、设计模型
字段类型
类型名 | python中的类型 | 类型说明 |
---|---|---|
Integer | int | 存储整形 |
SmallInteger | int | 小整形 |
BigInteger | int | 长整形 |
Float | float | 浮点形 |
String | str | varchar类型 |
Text | str | text长文本类型 |
Boolean | bool | tinyint 整形 |
Date | datetime.date | 日期 |
Time | datetime.time | 时间 |
DateTime | datetime.datetime | 日期和时间 |
可选约束条件
选项 | 说明 |
---|---|
primary_key | 默认False True创建主键索引 |
unique | 唯一索引 |
index | 常规索引 |
nullable | 是否可以为null 默认True |
default | 默认值 |
实例:
#创建模型
class User(db.Model):
__tablename__ = 'user' #表名默认为类名 给当前模型起表名
#id 字段名称 db.Column 整形 主键索引
id = db.Column(db.Integer,primary_key=True)
username = db.Column(db.String(20),index=True)
age = db.Column(db.Integer)
sex = db.Column(db.Boolean,default=True) #default默认值 并不是修改表结构 而是在插入数据的时候 没有给值 则将默认值插入
info = db.Column(db.String(100))
#添加构造方法
def __init__(self,username='',age=18,sex=True,info='默认值'):
self.username = username
self.age = age
self.sex = sex
self.info = info
创建模型
@app.route('/create_all/')
def create_all():
db.create_all()
return '创建表'
删除模型
#删除表 只删除和模型类重名的表
@app.route('/drop_all/')
def drop_all():
db.drop_all()
return '删除表'
四、增删改查
(1) 添加一条数据 add
#添加数据
@app.route('/insert_one/')
def insert_one():
# u = User(username='张三',age=18,info='张三的个人简介')
try:
u = User('李四',20,False,'李四的个人简介')
db.session.add(u)
db.session.commit() #提交
except:
db.session.rollback() #回滚
return '添加一条数据'
(2) 添加多条数据 add_all
#添加多条数据
@app.route('/add_many/')
def add_many():
u1 = User('王五',30)
u2 = User('赵六',40)
db.session.add_all([u1,u2])
db.session.commit() #提交
return '添加多条数据'
(3) 修改
#修改1
@app.route('/update/')
def update():
u = User.query.get(1) #查询id为1的数据对象
# print(u)
u.info = '坚持到无能为力'
db.session.add(u)
db.session.commit()
return '修改'
(4) 删除 delete
#删除
@app.route('/delete/')
def delete():
u = User.query.get(5)
db.session.delete(u) #删除
db.session.commit()
return '删除'
五、拆分MVT(简单的拆分)
project/
App/
__init__.py
views/
models/
settings.py #配置文件
exts.py #extensions 加载第三方扩展库
manage.py
六、设置提交方式
(1) 设置为自动提交
app.config[‘SQLALCHEMY_COMMIT_ON_TEARDOWN’] = True
不需要在db.session.commit()
(2) 自定义增删的基础类
class Base:
#自定义添加一条数据的方法
def save(self):
try:
db.session.add(self)
db.session.commit()
except:
db.session.rollback()
#自定义添加多条的方法
@staticmethod
def save_all(*args):
try:
db.session.add_all(args)
db.session.commit()
except:
db.session.rollback()
#自定义删除方法
def delete(self):
try:
db.session.delete(self)
db.session.commit()
except:
db.session.rollback()
使用
#创建模型
class User(Base,db.Model):
views.py
@view.route('/insert_one/')
def insert_one():
# u = User('JQKA',16,False)
# db.session.add(u)
# db.session.commit()
sexList = [True,False]
username = random.randrange(11111, 99999)
u1 = User(username, random.randint(1, 70), sexList[random.randint(0, 1)], str(username) + '的个人说明')
u1.save() #添加
return '添加数据'
@view.route('/insert_many/')
def insert_many():
sexList = [True,False]
# sexList[random.randint(0,2)]
username = random.randrange(11111,99999)
u1 = User(username,random.randint(1,70),sexList[random.randint(0,1)],str(username)+'的个人说明')
username = random.randrange(11111, 99999)
u2 = User(username, random.randint(1, 70), sexList[random.randint(0,1)], str(username) + '的个人说明')
# db.session.add_all([u1,u2])
User.save_all(u1,u2) #添加多条
return '添加多条数据'
@view.route('/delete/')
def delete():
User.query.get(12).delete()
return '删除'
七、数据库操作
查询集
查询数据的集合
分类
原始查询集
就是通过类名.query得到的就是原始查询集
数据查询集
通过过滤的方法 最终拿到的数据就三数据查询集
(1) all() 得到所有数据查询集 返回列表
类名.query.all()
@view.route('/all/')
def all():
data = User.query.all()
return render_template('show.html',data = data)
(2) filter() 过滤 默认没有条件 返回所有
类名.query.filter([类名.属性名 比较运算符 值。。。])
@view.route('/filter/')
def filter():
# data = User.query.filter() #返回所有
data = User.query.filter(User.age<60,User.age>10) #查询 年龄在59-11之间
return render_template('show.html',data=data)
(3) filter_by() 只支持单条件查询
没有条件 则返回所有
属性=值,,,,,,
类名.query.filter_by(username=’张三’….)
@view.route('/filter_by/')
def filter_by():
# data = User.query.filter_by() #返回所有
data = User.query.filter_by(sex=True) #查询性别为True的所有数据
return render_template('show.html',data=data)
(4) offset(num) 偏移量
@view.route('/offset/')
def offset():
data = User.query.offset(5) #偏移5条数据 从第六条开始取
return render_template('show.html',data=data)
(5) limit(num) 取值
@view.route('/limit/')
def limit():
data = User.query.limit(5) #取出5条数据
return render_template('show.html',data=data)
(6) order_by() 排序
默认升序
-类名.属性名
@view.route('/order_by/')
def order_by():
# data = User.query.order_by(User.age) #按照年龄升序
data = User.query.order_by(-User.age) #按照年龄降序
return render_template('show.html',data=data)
(7) first() 第一条
类名.query.first()
@view.route('/first/')
def first():
# data = User.query.first() #取出第一条数据
data = User.query.order_by(-User.age).first() #取出第一条数据
print(data)
return 'first'
(8) get(id值) 根据ID值进行查询
查询成功返回 对象
查询失败 返回None
@view.route('/get/')
def Get():
data = User.query.get(1)
print(data)
return 'get'
(9) contains() 包含关系
@view.route('/contains/')
def contains():
data = User.query.filter(User.username.contains('9')) #查询包含数字9的数据
return render_template('show.html',data=data)
(10) like 模糊查询
@view.route('/like/')
def like():
# data = User.query.filter(User.username.like('%9%')) #包含9的数据
# data = User.query.filter(User.username.like('%4')) #4作为结尾
data = User.query.filter(User.username.like('5%')) #5作为开头
return render_template('show.html',data=data)
(11) startswith 以…开头 endswith 以…结尾
@view.route('/with/')
def With():
# data = User.query.filter(User.username.startswith('9')) #以9作为开头
data = User.query.filter(User.username.endswith('4')) #以4作为结尾
return render_template('show.html',data=data)
(12) 比较运算符
-
__gt__
大于 -
__lt__
小于 -
__ge__
大于等于 -
__le__
小于等于 >=
<=
- ==
- !=
>
<
实例
@view.route('/bj/')
def bj():
#年龄 大于30 and 年龄小于50
data = User.query.filter(User.age.__gt__(30),User.age.__lt__(50))
return render_template('show.html',data=data)
(13) in 和 not in 在…范围内 不再 …范围内
@view.route('/in/')
def In():
#查询id在 1,2,3,4,12,13,14范围内的数据
# data = User.query.filter(User.id.in_([1,2,3,4,12,13,14]))
# 查询id不在 1,2,3,4,12,13,14范围内的数据
data = User.query.filter(~User.id.in_([1,2,3,4,12,13,14]))
return render_template('show.html',data=data)
(14) null 空
@view.route('/null/')
def null():
#查询username为null的数据
# data = User.query.filter(User.username.is_(None))
# data = User.query.filter(User.username==None)
#查询部位null的数据
# data = User.query.filter(User.username.isnot(None))
# data = User.query.filter(~User.username.is_(None))
data = User.query.filter(User.username!=None)
(15) and_ 逻辑与查询
from sqlalchemy import and_
默认为and查询
@view.route('/and/')
def And():
# data = User.query.filter(User.sex==True,User.id<10)
data = User.query.filter(and_(User.sex==True,User.id<10))
return render_template('show.html',data=data)
(16) or_ 逻辑或
from sqlalchemy import or_
@view.route('/or/')
def Or():
data = User.query.filter(or_(User.sex==True,User.id<10))
return render_template('show.html',data=data)
(17) and和 or的操作
@view.route('/or/')
def Or():
data = User.query.filter(and_(User.username.contains('9')),or_(User.sex==True,User.id<10))
#select * from user where username like '%9%' and (sex=1 or id<10)
return render_template('show.html',data=data)
(17) not_ 逻辑非
from sqlalchemy import not_
@view.route('/not/')
def Not():
#只能一个条件
data = User.query.filter(not_(User.sex==True))
return render_template('show.html',data=data)
(18) count 统计
@view.route('/count/')
def count():
#统计数据条数
data = User.query.count()
print(data)
return 'count'
# return render_template('show.html',data=data)
一、文件迁移
(1) 安装
sudo pip3 install flask-migrate
sudo pip3 install flask-script
(2) 使用
from flask_script import Manager
from flask_migrate import Migrate,MigrateCommand
migrate = Migrate(app) #实例化迁移对象
manager = Manager(app)
manager.add_command('db',MigrateCommand) #添加迁移命令
...
if __name__ == '__main__':
manager.run()
(3) 生成迁移目录
python3 manage.py db init
此刻会在项目中生成一个叫migrations的目录
(4) 生成迁移文件
python3 manage.py db migrate
(5) 执行迁移文件(更新数据库)
python3 manage.py db upgrade
三、拆分MVT的
blog/
App/
static/ #静态目录
img/
css/
js/
upload/
templates/ #模板目录
common/
base.html
models/ #模型目录
__init__.py
views/ #视图目录
__init.py
forms/ #表单目录
__init__.py
settings.py #配置文件
extensions.py #加载第三方扩展库
email.py #邮件
__init__.py #App包的初始化
migrations/ #迁移目录
venv/ #虚拟环境
manage.py #启动文件
分页显示博客
paginate 分页类 返回一个分页对象 pagination
paginate实例化传参
page 必须参数 当前页码
per_page 每页显示数据的条数 默认20
error_out 当查询出错的时候 是否报404的错误 默认为True
pagination对象
属性:
items 当页的所有数据
page 当前页码
pages 总页码是
total 总数据条数
per_page 每页多少条
prev_num 上一页的页码
next_num 下一页的页码
has_prev 是否有上一页
has_next 是否有下一页
方法:
prev 上一页的分页对象
next 下一页的分页对象
iter_pages 是一个迭代器 返回所有的页码数 显示不下则显示 ...
缓存
安装:
sudo pip3 install flask-cache
使用
from flask_cache import Cachce
cache = Cache(app,config={“CACHE_TYPE”:”缓存类型”})
实例
extension.py
from flask_cache import Cache
cache = Cache(config={"CACHE_TYPE":'simple'})#实例化缓存 简单的缓存
cache.init_app(app=app) #缓存
main.py
from App.extensions import cache
@main.route('/')
@cache.cached(timeout=120) #timeout 过期的时间
def index():
print('你能看到我几次。。。')
缓存类型
- @cache.cached(timeout=120) #只是缓存页面
- @cache.memoize(timeout=120) #会根据不同传参进行不同的缓存
改正之后的缓存
from flask import Blueprint,render_template,request,current_app,redirect,url_for
from App.models import Posts
from App.extensions import cache
main = Blueprint('main',__name__)
@main.route('/')
# @cache.cached(timeout=120)
def index():
return redirect(url_for('main.main_cache',page=1))
@main.route('/show/<int:page>/')
@cache.memoize(timeout=120) #根据不同参数进行数据的缓存
def main_cache(page):
print('你能看到我几次。。。')
# try:
# page = int(request.args.get('page', 1))
# except:
# page = 1
p = Posts.query.filter_by(pid=0).order_by(Posts.timestamp.desc()).paginate(page, current_app.config['CON_NUM'],False)
data = p.items # 当前页的数据
return render_template('main/index.html', data=data, pagination=p)
清除缓存的办法
(1) 在settings.py中 配置缓存时间
CACHE_DEFAULT_TIME
(2) 给装饰器添加timeout参数 时间秒
@cache.cached(timeout=120)
@cache.memoize(timeout=120) #根据不同参数进行数据的缓存
(3) 清除所有缓存
cache.clear()
(4) 只清除 memoize的缓存
cache.delete_memoized(缓存的视图函数名称)
(5) 只清除 cached的缓存
cache.delete(key_prefix 缓存前缀)
表多对多的使用
添加多对多数据
u1 = User.query.get(1)
p1 = Posts.query.get(1)
#1号用户 收藏 1号帖子
u1.favorites.append(p1)
查看多对多数据
u1.favorites.all() #查看用户1收藏了那些帖子
p1.users.all() #查看帖子被哪些用户收藏
删除多对多数据
u1 = User.query.get(1)
p1 = Posts.query.get(1)
u1.favorites.remove(p1)
上线部署
(1) 安装
在要安装项目的目录创建虚拟环境
virtualenv venv
source activate # 开启虚拟开发环境模式
pip3 install uwsgi # 安装uwsgi
(2) 配置
uwsgi配置文件支持很多格式,我采用.ini格式,命名为uconfig.ini具体内容如下:
[uwsgi]
# 外部访问地址,可以指定多种协议,现在用http便于调试,之后用socket
socket = 0.0.0.0:8000 # uwsgi的监听端口
# 指向项目目录
chdir = /home/xlg/test/
# flask启动程序文件
wsgi-file = manage.py
# flask在manage.py文件中的app名
callable = app
plugins = python# 这行一定要加上,不然请求时会出现-- unavailable modifier requested: 0 --错误提示
# 处理器数
processes = 1
# 线程数
threads = 2
#状态检测地址
stats = 127.0.0.1:9191
(3) 安装 nginx
Nginx:sudo apt-get install nginx
Nginx是轻量级、性能强、占用资源少,能很好的处理高并发的反向代理软件。Ubuntu 上配置 Nginx 也是很简单,不要去改动默认的 nginx.conf 只需要将/etc/nginx/sites-available/default
文件替换掉就可以了。
新建一个 default 文件:
server{
listen 80; # 服务器监听端口
server_name 192.168.100.136; # 这里写你的域名或者公网IP
location / {
uwsgi_pass 127.0.0.1:8000; # 转发端口,需要和uwsgi配置当中的监听端口一致
include uwsgi_params; # 导入uwsgi配置
#uwsgi_param UWSGI_PYTHON /home/自己创建的目录/venv; # Python解释器所在的路径(这里为虚拟环境)
uwsgi_param UWSGI_PYTHON /usr/bin/python3;
uwsgi_param UWSGI_CHDIR /home/xlg/test/;# # 自己创建的目录 项目根目录
uwsgi_param UWSGI_SCRIPT manage:app; # 指定启动程序
#比如你测试用test.py文件,文件中app = Flask(name),那么这里就填 test:app
}
}
服务启动
- sudo service start
- sudo service stop
- sudo service restart
指定配置文件,后台运行 uwsgi, 这时再刷新一下之前打开的页面,就可以看到应用正常运行了。
uwsgi uconfig.ini
访问地址
192.168.100.136