1.web框架
web后端:
Web框架:
vue,jquery,bootstrap
flask:
django:
tornado:
flask:
方向:
1. mtv 带模板html 模版语言
2. 前后端分离 只负责写接口 api
REST: get post put patch delete
搭建框架:
1.要有虚拟环境:
linux: virtualenv virtualenvwrapper
配置文件的修改: .Envs
.bashrc
...
windows:pip install virtualenvwrapper-win
mkvirutalenv mytest1 新建虚拟环境
workon 虚拟环境名
deactivate 退出虚拟环境
rmvirutalenv 虚拟环境名
2. 新建虚拟环境:
将项目切换到虚拟环境上
点击终端:(flaskenv) C:\Users\running\Desktop\GPDay41\代码\day41_flask>
说明进入到虚拟环境中....
2.Flask框架
from flask import Flask
import settings
app = Flask(__name__)
# 根据配置文件设置
app.config.from_object(settings)
# 装饰器 路由 URL http://127.0.0.1:5000/
@app.route('/')
def index(): # 视图函数 mtv: view 视图 函数
return '大家好!哈哈哈哈哈'
def deal():
return ''
# WSGI: Python Web Server Gateway Interface 是为Python语言定义的Web服务器和Web应用程序或框架之间的一种简单而通用的接口
# flask 内置服务器
if __name__ == '__main__':
# host 设置为'0.0.0.0' 可以让外界访问 debug=True----当代吗改变时自动加载,默认关闭
app.run(host='0.0.0.0', port=5000, debug=True) # 设置端口号最好在启动之前设置
3.flask环境搭建
from flask import Flask
import settings
app = Flask(__name__)
app.config.from_object(settings)
# 多个路由
@app.route('/')
def hello_world1():
return 'Hello World111111!'
@app.route('/index')
def hello_world2():
return 'Hello World222222!'
@app.route('/test')
def hello_world3():
return '<font color="red"> Hello World333333! </font>'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)
3
app= Flask(__name__)
1.关联服务器并启动
app.run(host='',port=5000)
在终端: python app.py
2. app.route('/') 注册路由
def func():
return .....
4.配置
# 配置文件
ENV = 'development'
DEBUG = True
配置:
Environment: production
production ----》 正在
development ---》开发
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
debug mode:on
run(deubg=True) ----> mode:on
on的模式: 代码有改变则服务器重启(重新加载程序)
5.导入配置文件
在app启动前引入文件(两种):
1. app.config.from_object(settings)
2. app.config.from_pyfile('settings.py')
flask是基于Werkzeug工具包的一个web服务框架,所以flask里视图函数的实现,实际上是对werkzeug的一层封装
路由结合视图函数
@app.route('/')
def hello_world(): # ---->视图函数
return 'HELLO hello world!hello kitty!'
#endpoint 相当于起了个别名
@app.route('/abc',endpoint='abc1')
def show_abc():
return '<h1>abc</h1>'
# route就是将函数与add_url_rule进行了装饰
def show_name():
return '千锋教育'
app.add_url_rule('/name', view_func=show_name)
路径(路由)的变量规则:
转换器:
str 默认
int 整型
float 浮点型
path 类似str,但是可以识别'/'
uuid 识别uuid类型的字符串
@app.route('/news/<int:num>') ----><int:num>表示的就是一个变量
# url_for:反向解析
@app.route('rule',endpoint='value')
def func():
pass
url_for('endpoint') -----------> 根据endpoint找到路由rule
6.请求和响应
请求request,响应response
请求:request
from flask import request
client 发出的请求
request对象 只要有请求则会产生一个request对象
request.method 获取请求的方式
request.args.get('key',默认值) ----> get
request.form.get('key',默认值) ----> post
request.values ----> [dictA,dictB] dictA---GET dictB--POST
request.get_json() application/json 类型数据
request.get_data() 将数据构成一个字节字符串
request.remote_addr 获取远程的ip地址
print(request.path) # /
print(request.full_path) # /?name=admin
print(request.url) # http://127.0.0.1:5000/?name=admin
print(request.base_url) # http://127.0.0.1:5000/
print(request.url_root)
print(request.method) # 'GET'
print(request.query_string) # b'name=admin'
响应:response
在视图函数的返回值后面可以跟:
1.string 系统会自动将str转成一个response对象
2.make_reponse( ) 构建response对象,可以给response对象添加一些头部信息
3.jsonify(datas) 将datas转成json的格式 dict默认使用此函数
4.render_template()
5.redirect()
注:make_response()
注意:make_response 想要返回页面,不能直接写做:make_response('hello.html'),必须用render_template('hello.html')形式。
例一:
@blue.route('/makeresponse/')
def make_response_function():
response = make_response('<h2>羞羞哒</h2>')
return response, 404
例二:
@blue.route('/makeresponse/')
def make_response_function():
temp = render_template('hello.html')
response = make_response(temp)
return response
返回状态码的方式: 1》 response = make_response(temp, 200)
2》 return response, 200
8.web中状态码
1.web中的请求状态码:
201创建或更新数据成功
204删除数据成功
301永久重定向,多次发请求时,直接进入定向后的网址,相当于一次请求
302临时重定向,一次请求时,请求完后,进入定向的网址,相当于两次请求303表示对post请求进行重定向
307表示对get请求进行重定向
400 前端请求错误401 用户未进行登录认证
403 无访问权限
404请求未找到
405 请求方式不允许406 服务器无法返回请求制定的accept字段格式。
503 服务异常,可能是服务器负载过重
507 服务器存储错误
- 请求方式post,get,head,options,put,delete区别
get请求方式一般用于查询数据。
post请求一般用于表单或json数据更改,安全性更高。可以在请求体中增加一些数据。put 一般用于更新数据
delete请求用于删除数据
options 查询信息,查询服务端的支持的请求方式。
9.路由
# @router() 与 add_url_rule(rule, endpoint, f, **options)
from flask import Flask
route:
def route(self, rule, **options):
def decorator(f):
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
@app.route('/index')
def index():
return 'welcome everyone!'
# 这个装饰器其实就是将rule字符串跟视图函数进行了绑定,通过add_url_rule()实现的绑定
# 等效的
def index():
return 'welcome everyone!'
app.add_url_rule('/index', view_func=index)
2.路由的变量规则:
string (缺省值) 接受任何不包含斜杠的文本 *
int 接受正整数 *
float 接受正浮点数
path 类似 string ,但可以包含斜杠
uuid 接受 UUID 字符串
# 字符串类型不可自定,是默认的,而其他类型必须自定
@app.route('/add/<int:num>')
def add(num):
print('--->', type(num))
result = num + 10
return str(result)
@app.route('/add1/<float:money>')
def add1(money):
print('====>', type(money))
return str(money)
@app.route('/index/<path:p>')
def get_path(p):
print('******>', type(p)) # str类型
print(p)
return p
@app.route('/test/<uuid:uid>') # 必须传递uuid的格式,uuid模块, uuid.uuid4() ---->UUID类型
def test(uid):
print('#######>>>>>', type(uid))
return '获取唯一的标识码'
注意:uuid类型----------
import uuid
uid = uuid.uuid4()
print(uid)
print(type(uid))
uid1 = str(uid)
print(type(uid1))
uid1 = uid1.replace('-','')
print(uid1)
10.有参数的路由
@app.route('/getcity/<key>') # key就是一个变量名,默认是字符串类型的
def get_city(key): # 参数是必须添加的
print(type(key))
return data.get(key)
11.视图函数
#类型一:
@app.route('/index')
def index():
return {'a': '<h1>北京</h1>', 'b': '上海', 'c': '深圳'} # application/json,标签内容原样显示
#类型二:
@app.route('/index1')
def index1():
return '<h1>北京</h1>'
#类型三:
@app.route('/index2')
def index2():
# ValueError: not enough values to unpack (expected 2, got 1)
# return ('beijing', 'shanghai', 'shenzhen')
return ('beijing',200)
#类型四:
@app.route('/index3')
def index3():
return Response('<h1>大家想好中午吃什么了吗?</h1>') # 返回的Response对象
# 返回值: # Content-Type:text/html; charset=utf-8
# return 后面返回的字符串其实也是做了一个response对象的封装。最终的返回结果还是response对象
# return 的返回值可以是:string dict pulple response对象 WSGI
# 必须要有返回值,不能为None
返回值总结:
1.str
2.dict
3.Response
4.render_template()
5.redirect()
6.make_response()
12.路由路径
# 所有的路由搜索规则都是自上而下搜索,在写路由的是有定义的路由是唯一的。
# 路由中定义'/',无论请求的URL是否带有/,都可以执行视图函数。如果请求的是有/,浏览器做了一次重定向
@app.route('/projects/')
def projects():
return 'The project page'
@app.route('/projects') # 请求路由中如果添加了/ http://127.0.0.1:5000/projects/ 显示Not Found
def about():
return 'The about page'
# 不建议带/,我们希望请求路径是惟一的,
# 以上面这种方式写,第二个将永远不会执行
13.渲染模板
# render_template 渲染模板,就是将h5文件转换为字符串,<class 'str'>
@app.route('/register')
def register():
r = render_template('register.html') # 默认去模板文件夹中找文件夹的,怎么就知道文件夹就是templates?
# print(r)
return r
# 注意:一个pycharm的bug,h5文件写了,但显示不出来,报错,需要重启或重新创建h5文件
14.get 与 post
@app.route('/register2', methods=['GET', 'POST'])
def register2(): # 获取页面提交的内容
print(request.full_path) # /register2?username=zhangsan&address=Beijing
print(request.path) # /register2
print(request.args) # dict类型 d = {'a':'aaa','b':'7878'} d.get('b') 只能取到get请求的
# print(request.args.get('username')) # 获取值
# print(request.args.get('address'))
print(request.form) # 如果请求方法是post则需要通过request.form取值
print(request.form.get('username'))
print(request.form.get('address'))
return '进来了'
15.重定向
redirect()
返回值为response对象,Supported codes are 301, 302, 303, 305, 307, and 308。300 is not supported
作用:redirects the client to the target location
参数:(location, code=302, Response=None)
工作过程:1.向浏览器返回 状态码和location
2.浏览器接收到状态码后,重定向后请求location地址,服务器响应请求地址
url_for('地址别名')
作用:反向解析(通过别名解析地址),返回一个地址
可以写为:
return redirect(url_for('地址别名'))
16.模板-1
render_template(template_name_or_list, **context) 渲染模板
模板:(网页)
模板的语法:
1. 在模板中获取view中传递的变量值:{{ 变量名key }}
2. 向模板传递参数: render_template('模板名字',key=value,key=value)
@app.route('/show')
def show():
name = '沈凯' # str
age = 18 # int
friends = ['建义', '陈璟', '小岳岳', '郭麒麟'] # list
dict1 = {'gift': '大手镯', 'gift1': '鲜花', 'gift2': '费列罗'} # dict
# 创建对象
girlfriend = Girl('美美', '安徽阜阳')
return render_template('show.html', name=name, age=age, gender='男', friends=friends, dict1=dict1, girl=girlfriend)
参数总结:参数可以有多个,可以是 str list int dict 自定义类型 json
模板:
{{ list.0 }} 同 {{ list[0] }}
{{ dict.key }} 同 {{ dict.get(key) }}
{{ girl.name }} 同 {{ 对象.属性 }}
3. 控制快:
{% if 条件 %}
{% endif %}
{% if 条件 %}
条件为True
{% else %}
条件为False
{% endif %}
{% for 变量 in 可迭代的对象 %}
for循环要做的任务
{% endfor %}
可以使用loop变量
loop.index 序号从1开始
loop.index0 序号从0开始
loop.revindex reverse 序号是倒着的
loop.revindex0
loop.first 布尔类型 是否是第一行
loop.last 布尔类型 是否是第二行
4。
17.过滤器
# 过滤器的本质就是函数
模板语法中过滤器:
{{ 变量名 | 过滤器(*args) }}
{{ 变量名 | 过滤器 }}
常见的过滤器:
1。 safe : 禁用转译
msg = '<h1>520快乐!</h1>'
return render_template('show_2.html', girls=girls, users=users, msg=msg)
不想让其转译:
{{ msg | safe }}
2。 capitalize:单词的首字母大写
{{ n1 | capitalize }}
3。lower和upper
大小写的转换
4。title 一句话中每个单词的首字母大写
msg = 'She is a beautiful girl'
{{ msg | title}}
5。reverse 翻转
{{ n1 | reverse}}
6。format
{{ '%s is %d years old' | format('lily',18) }}
7.truncate 字符串截断
list的操作:
{# 列表过滤器的使用 #}
{{ girls | first }}<br>
{{ girls | last }}<br>
{{ girls | length }}<br>
{#{{ girls | sum }} 整型的计算 #}
{{ [1,3,5,7,9] | sum }}<br>
{{ [1,8,5,7,3] | sort }}<br>
dict:
{% for v in users.0.values() %} ---->获取值
<p>{{ v }}</p>
{% endfor %}
<hr>
{% for k in users.0.keys() %} ----》获取键
<p>{{ k }}</p>
{% endfor %}
<hr>
{% for k,v in users.0.items() %} ---》获取键值
<p>{{ k }}---{{ v }}</p>
{% endfor %}
2》 自定义过滤器:
# 过滤器本质就是函数
1. 通过flask模块中的add_template_filter方法
a. 定义函数,带有参数和返回值
b. 添加过滤器 app.add_template_filter(function,name='')
c. 在模板中使用: {{ 变量 | 自定义过滤器 }}
2。使用装饰器完成
a. 定义函数,带有参数和返回值
b. 通过装饰器完成,@app.template_filter('过滤器名字')装饰步骤一的函数
c. 在模板中使用: {{ 变量 | 自定义过滤器 }}
例:
# 第一种方式
def replace_hello(value):
print('------>', value)
value = value.replace('hello', '')
print('======>', value)
return value.strip() # 将 替换的结果返回
app.add_template_filter(replace_hello, 'replace') // 此处replace为replace_hello的别名
# 第二种方式 装饰器
@app.template_filter('listreverse')
def reverse_list(li):
temp_li = list(li)
temp_li.reverse()
return temp_li
18.模板复用
1》继承:
模板继承:
需要模版继承的情况:
1。 多个模板具有完全相同的顶部和底部
2。 多个模板具有相同的模板内容,但是内容中部分不一样
3。 多个模板具有完全相同的模板内容
标签:
{% block 名字 %}
{% endblock %}
1.定义父模板
2.子模板继承父模板
步骤:
父模板:
1。 定义一个base.html的模板
2。 分析模板中哪些是变化的比如:{% block title %}父模板的title{% endblock %}
对变化的部分用block进行"预留位置"也称作:挖坑
3。注意:样式和脚本 需要提前预留
{% block mycss %}{% endblock %}
{% block myjs %}{% endblock %}
子使用父模板:
1。 {% extends '父模板的名称' %}将父模板继承过来
2。 找到对应的block(坑)填充,每一个block都是有名字的。
2》include
include: 包含
在A,B,C页面都共同的部分,但是其他页面没有这部分。
这个时候考虑使用include
步骤:
1。先定义一个公共的模板部分,xxx.html
2。谁使用则include过来, {% include '文件夹/xxx.html' %}
3》宏(macro)
宏:macro
1。把它看作是jinja2的一个函数,这个函数可以返回一个HTML字符串
2。目的:代码可以复用,避免代码冗余
定义两种方式:
1。在模板中直接定义:
类似: macro1.html 中定义方式
2。将所有宏提取到一个模板中:macro.html
谁想使用谁导入:
{% import 'macro.html' as xxx %}
{{ xxx.宏名字(参数) }}
例:
{# 定义宏 #}
{% macro form(action,value='登录',method='post') %}
<form action="{{ action }}" method="{{ method }}">
<input type="text" placeholder="用户名" name="username"><br>
<input type="password" placeholder="密码" name="password"><br>
<input type="submit" value="{{ value }}">
</form>
{% endmacro %}
{# 调用宏 #}
{{ form('/') }}
# 分文件调用宏
{% import 'macro/macro.html' as func %}
{{ func.form('/welcome',value='注册') }}
19 创建项目
-
创建项目并设置settings.py文件
-
删除app.py中的部分内容
-
创建包apps,并在包中__init__.py中创建app的工厂函数
def create_app(): app = Flask(__name__) # app是一个核心对象 app.config.from_object(settings) # 加载配置 # 蓝图 return app -
在apps中创建项目的程序包比如user,goods,order等
-
以user为例,在user包中创建view.py,在view.py中定义蓝图
user_bp = Blueprint('user', __name__) # 其中第一个参数是蓝图的名字,第二个参数是import_name -
使用蓝图定义路由和视图函数
@user_bp.route('/') def user_center(): return render_template('user/show.html', users=users)注意在蓝图中进行了模板的加载,而模板的文件夹默认是跟flask对象是同级的,如果不是同级的注意在Flask对象创建的时候初始化设置
-
注册蓝图到app对象上
def create_app(): app = Flask(__name__,template_folder='../templates',static_folder='../static') # app是一个核心对象 app.config.from_object(settings) # 加载配置 # 蓝图 app.register_blueprint(user_bp) # 将蓝图对象绑定到app上 print(app.url_map) return app -
补充删除操作的步骤:
- 点击删除按钮或者删除链接
- 给链接添加单击事件,可以通过js或者jquery
<a href="javascript:;" onclick="del('{{ user.username }}')">删除</a>
- 在js中定义函数:
function del(username){
// console.log(username)
// location 地址栏对象
location.href = '/del?username='+username
}
-
触发/del的路由函数
@user_bp.route('/del') def del_user(): # 获取你传递的username username = request.args.get('username') # 根据username找到列表中的user对象 for user in users: if user.username == username: # 删除user users.remove(user) return redirect('/') else: return '删除失败'
20 设置变量
1.set :相当于设置全局变量
{% set username ='zhangsan' %}
{{ username }}
2.with: 相当于设置局部变量
{% with num=1000 %}
{{ num }}
{% endwith %}
21.蓝图
from app.user.views import user_bp # 先导入才能绑定
# 将蓝图绑到app上
app.register_blueprint(user_bp)
# 创建用户蓝图对象
user_bp=Blueprint('user',__name__)
# 其中第一个参数是蓝图的名字,第二个参数是import_name
22 完整的创建项目
步骤:
1。配置数据库的连接路径
# mysql+pymysql://user:password@hostip:port/databasename
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:root@127.0.0.1:3306/flaskday05'
2。flask-sqlalchemy的搭建:
创建包ext
__init__.py中添加:
db = SQLAlchemy() ---->必须跟app联系
def create_app():
....
db.init_app(app)
return app
3. flask-migrate的配置:
migrate = Migrate(app=app, db=db)
manager.add_command('db', MigrateCommand)
4.创建模型:
models.py
模型就是类,经常称作模型类
class User(db.Model): ------> user表
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(15), nullable=False)
password = db.Column(db.String(12), nullable=False)
phone = db.Column(db.String(11), unique=True)
rdatetime = db.Column(db.DateTime, default=datetime.now)
5.使用命令:
a. ************敲黑板***************
在app.py 中导入模型:from apps.user.models import User
b. 在终端使用命令:db
python3 app.py db init -----》 产生一个文件夹migrations
python3 app.py db migrate -----> 自动产生了一个版本文件
项目
| ---apps
| ---ext
| ---migrations python3 app.py db init 只需要init一次
|---versions 版本文件夹
|---71edde7ee937_.py ---》 python3 app.py db migrate 迁移
|---cc0dca61130f_.py
python3 app.py db upgrade 同步
python3 app.py db downgrade 降级
6.添加数据:以注册为例:
# 模板,视图与模型结合
# 1>. 找到模型类并创建对象
user = User()
# 2>. 给对象的属性赋值
user.username = username
user.password = password
user.phone = phone
# 添加
# 3>.将user对象添加到session中(类似缓存)
db.session.add(user)
# 4>.提交数据
db.session.commit()
23 数据库操作
1.查询:
查询所有: 模型类.query.all() ~ select * from user;
如果有条件的查询:
模型类.query.filter_by(字段名 = 值) ~ select * from user where 字段=值;
模型类.query.filter_by(字段名 = 值).first() ~ select * from user where 字段=值 limit..;
select * from user where age>17 and gender='男';
select * from user where username like 'zhang%';
select * from user where rdatetime> xxx and rdatetime < xxx;
模型类.query.filter() 里面是布尔的条件 模型类.query.filter(模型名.字段名 == 值)
模型类.query.filter_by() 里面是一个等值 模型类.query.filter_by(字段名 = 值)
***** 模型类.query.filter() ******
1. 模型类.query.filter().all() -----> 列表
2. 模型类.query.filter().first() ----->对象
3.User.query.filter(User.username.endswith('z')).all() select * from user where username like '%z';
User.query.filter(User.username.startswith('z')).all() # select * from user where username like 'z%';
User.query.filter(User.username.contains('z')).all() # select * from user where username like '%z%';
User.query.filter(User.username.like('z%')).all()
多条件:
from sqlalchemy import or_, and_,not_
并且: and_ 获取: or_ 非: not_
User.query.filter(or_(User.username.like('z%'), User.username.contains('i'))).all()
类似: select * from user where username like 'z%' or username like '%i%';
User.query.filter(and_(User.username.contains('i'), User.rdatetime.__gt__('2020-05-25 10:30:00'))).all()
# select * from user where username like '%i%' and rdatetime < 'xxxx'
补充:__gt__,__lt__,__ge__(gt equal),__le__ (le equal) ----》通常应用在范围(整型,日期)
也可以直接使用 > < >= <= !=
User.query.filter(not_(User.username.contains('i'))).all()
18 19 20 17 21 22 ....
select * from user where age in [17,18,20,22];
排序:order_by
user_list = User.query.filter(User.username.contains('z')).order_by(-User.rdatetime).all() # 先筛选再排序
user_list = User.query.order_by(-User.id).all() 对所有的进行排序
注意:order_by(参数):
1。 直接是字符串: '字段名' 但是不能倒序
2。 填字段名: 模型.字段 order_by(-模型.字段) 倒序
限制: limit
# limit的使用 + offset
# user_list = User.query.limit(2).all() 默认获取前两条
user_list = User.query.offset(2).limit(2).all() 跳过2条记录再获取两条记录
总结:
1. User.query.all() 所有用户
2. User.query.get(主键) 一个用户
3. User.query.filter() * ???????
如果要检索的字段是字符串(varchar,db.String):
User.username.startswith('')
User.username.endswith('')
User.username.contains('')
User.username.like('')
User.username.in_(['','',''])
User.username == 'zzz'
如果要检索的字段是整型或者日期类型:
User.age.__lt__(18)
User.rdatetime.__gt__('.....')
User.age.__le__(18)
User.age.__ge__(18)
User.age.between(15,30)
多个条件一起检索: and_, or_
非的条件: not_
排序:order_by()
获取指定数量: limit() offset()
4. User.query.filter_by()
2.删除:
两种删除:
1。逻辑删除(定义数据库中的表的时候,添加一个字段isdelete,通过此字段控制是否删除)
id = request.args.get(id)
user = User.query.get(id)
user.isdelete = True
db.session.commit()
2。物理删除(彻底从数据库中删掉)
id = request.args.get(id)
user = User.query.get(id)
db.session.delete(user)
db.session.commit()
3.更新:
id = request.args.get(id)
user = User.query.get(id)
# 修改对象的属性
user.username= xxxx
user.phone =xxxx
# 提交更改
db.session.commit()
4.增加:
id = request.args.get(id)
username = request.args.get('username')
password = request.args.get('password')
user = User.query.get(id)
# 创建对象(表)
user = User()
user.username = username
user.password = password
# 添加
db.session.add(user)
db.session.commit()
24 多表连接
主要理解多张数据库表的关系。
明确:一个项目肯定会有多张表,确定表与表之间的关系最重要。
在开始项目前必须要确定表与表的关系
> 在flask的框架中如何体现1对多的模型关系?
> 就是通过外键ForignKey和relationship体现。ForignKey是给映射关系说的,relationship是给模板使用的。
------> 一对多:
class User(db.Model):
....
articles = db.relationship('Article', backref='user') # 写一个就行
...
---》 多对多
》方式一:
```python
tags = db.Table('tags',
db.Column('tag_id', db.Integer, db.ForeignKey('tag.id')),
db.Column('page_id', db.Integer, db.ForeignKey('page.id'))
)
class Page(db.Model):
id = db.Column(db.Integer, primary_key=True)
tags = db.relationship('Tag', secondary=tags,backref=db.backref('pages', lazy='dynamic'))
class Tag(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20),nullable=False)
```
》方式二:
```python
class Page(db.Model):
id = db.Column(db.Integer, primary_key=True)
tags = db.relationship('Tag', secondary='Page_tag',
backref='pages')
class Tag(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20),nullable=False)
class Page_tag(db.Model):
id = db.Column(db.Integer, primary_key=True)
db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'))
db.Column('page_id', db.Integer, db.ForeignKey('page.id'))
```
25 flask-bootstrap
# flask-bootstrap的使用:
使用flask-bootstrap:
步骤:
1. pip install flask-bootstrap
2. 进行配置:
```python
from flask-bootstrap import Bootstrap
bootstrap = Bootstrap()
# 在__init__.py中进行初始化:
###### 初始化bootstrap
bootstrap.init_app(app=app)
``
3. 内置的block:
```python
{% block title %}首页{% endblock %}
{% block navbar %} {% endblock %}
{% block content %} {% endblock %}
{% block styles %} {% endblock %}
{% block srcipts %} {% endblock %}
{% block head %} {% endblock %}
{% block body %} {% endblock %}
```
4. 创建base.py
```python
{% extends "bootstrap/base.html" %}
{% block title %}首页{% endblock %}
{% block styles %}
{{ super() }}
<style>
.....
</style>
{% endblock %}
{% block navbar %}
....
{% endblock %}
{% block content %}
{% block newcontent %}
<h1>Hello, Bootstrap</h1>
{% endblock %}
{% block footer %}
<p id="myfoot">京ICP备11008000号-6京公网安备11010802020853</p>
{% endblock %}
{% endblock %}
5. 子模板继承父模板:
{% extends ‘base.html’ %}
{% block title %}
博客首页
{% endblock %}
{% block styles %}
{{ super() }}
{% endblock %}
{% block newcontent %}
{% endblock %}
26 会话机制-cookie
# 会话机制
## (1)cookie
在网站中,HTTP请求是无状态的。也就是说,即使第一次用户访问服务器并登录成功后,第二次请求服务器依然不知道当前发起请求的是哪个用户。cookie的出现就是为了解决这个问题,第一次登录后服务器返回一些数据(cookie)给浏览器,浏览器将这些数据保存在本地。当用户发起第二次请求的时候,浏览器自动的将上次请求得到的cookie数据携带给服务器,服务器通过这些cookie数据就能分辨出当前发起请求的是哪个用户了。cookie存储的数据量有限,不同的浏览器有不同的存储大小,但一般不超过4K,因此使用cookie只能存储一些少量的数据。
## (2)session
session与cookie的作用有点类似,都是为了存储用户相关的信息。不同的是,cookie是存储在本地浏览器,session存储在服务器。存储在服务器的数据会更加的安全,不容易被窃取。但存储在服务器也有一定的弊端,就是会占用服务器的资源。
## (3)cookie和session的结合使用
web开发发展至今,cookie和session的使用已经出现了一些非常成熟的方案。在如今的市场和企业里,一般有两种存储方式:
- 存储在服务器:通过cookie存储一个session_id,然后具体的数据则保存在session中。当用户已经登录时,会在浏览器的cookie中保存一个session_id,下次再次请求的时候,会把session_id携带上来,服务器根据session_id在session库中获取用户的session数据,从而能够辨别用户身份,以及得到之前保存的状态信息。这种专业术语叫做server side session(服务器端会话).
- 将session数据加密,然后存储在cookie中。这种专业术语叫做client side session(客户端会话),flask采用的就是这种方式,但是也可以替换成其它形式.
## 实现方式:
1. ### cookie方式:
- 保存:set_cookie
通过response对象保存。
response = redirect(xxx)
response = render_template(xxx)
response = Response()
response = make_response()
response = jsonify()
通过对象调用方法
response.set_cookie(key,value,max_age)
其中max_age表示过期时间,单位是秒
也可以使用expires设置过期时间,expires=datetime.now()+timedelta(hour=1)
- 获取:cookies.get(key)
通过request对象获取。
request.form.get()
request.args.get()
cookie也在request对象中
> request.cookies.get(key) ----> value
- 删除:delete_cookie(key)
通过response对象删除。 把浏览器中的key=value删除了
response = redirect(xxx)
response = render_template(xxx)
response = Response()
response = make_response()
response = jsonify()
通过对象调用方法
> response.delete_cookie(key)
27 会话机制-session
2.session方式:
session: 是在服务器端进行用户信息的保存。一个字典
注意:
使用session必须要设置配置文件,在配置文件中添加 -- SECRET_KEY='xxxxx',
添加SECRET_KEY的目的就是用于sessionid的加密。如果不设置会报错。
- 设置:
如果要使用session,需要直接导入:
from flask import session
把session当成字典使用,因此:session[key]=value
就会将key=value保存到session的内存空间
并会在响应的时候自动在response中自动添加有一个cookie:session=加密后的id
- 获取
用户请求页面的时候就会携带上次保存在客户端浏览器的cookie值,其中包含session=加密后的id
获取session值的话通过session直接获取,因为session是一个字典,就可以采用字典的方式获取即可。
value = session[key] 或者 value = session.get(key)
> 这个时候大家可能会考虑携带的cookie怎么用的????
> 其实是如果使用session获取内容,底层会自动获取cookie中的sessionid值,
> 进行查找并找到对应的session空间
- 删除
> session.clear() 删除session的内存空间和删除cookie
> del session[key] 只会删除session中的这个键值对,不会删除session空间和cookie
28 短信验证码
1.短信息发送:
浏览器请求---手机号---》服务器--手机号--》【阿里云短信服务】---验证码---》用户手机
《--------返回验证码和状态码《-------- 《-----
---手机号和验证码---》 服务器(开始处理)
# 发送短信息
@user_bp1.route('/sendMsg')
def send_message():
phone = request.args.get('phone')
# 补充验证手机号码是否注册,去数据库查询
SECRET_ID = "dcc535cbfaefa2a24c1e6610035b6586" # 产品密钥ID,产品标识
SECRET_KEY = "d28f0ec3bf468baa7a16c16c9474889e" # 产品私有密钥,服务端生成签名信息使用,请严格保管,避免泄露
BUSINESS_ID = "748c53c3a363412fa963ed3c1b795c65" # 业务ID,易盾根据产品业务特点分配
api = SmsSendAPIDemo(SECRET_ID, SECRET_KEY, BUSINESS_ID)
params = {
"mobile": phone,
"templateId": "10084",
"paramType": "json",
"params": "json格式字符串"
}
ret = api.send(params) # ret 为字典
# phone 为变量,存储的使用户登录手机号
session[phone] = '189075'
return jsonify(code=200, msg='短信发送成功!')
# if ret is not None:
# if ret["code"] == 200:
# taskId = ret["result"]["taskId"]
# print("taskId = %s" % taskId)
# session[phone]='189075'
# return jsonify(code=200, msg='短信发送成功!')
# else:
# print("ERROR: ret.code=%s,msg=%s" % (ret['code'], ret['msg']))
# return jsonify(code=400, msg='短信发送失败!')
29 登录权限验证
2.登录权限的验证
只要走center路由,判断用户是否是登录状态,如果用户登录了,可以正常显示页面,如果用户没有登录
则自动跳转到登录页面进行登录,登录之后才可以进行查看。
30 Flask钩子函数
3.钩子函数:
直接应用在app上:
before_first_request
before_request
after_request
teardown_request
应用到蓝图:
before_app_first_request
before_app_request
after_app_request
teardown_app_request
31 文件上传
3.文件上传
A. 本地上传
注意:
表单: enctype="multipart/form-data"
<form action="提交的路径" method="post" enctype="multipart/form-data">
<input type="file" name="photo" class="form-control">
<input type="submit" value="上传相册" class="btn btn-default">
</form>
view视图函数:
photo = request.files.get('photo') ----》photo是FileStorage
属性和方法:FileStorage = 》fs
fs.filename
fs.save(path) ----> path上传的路径os.path.join(UPLOAD_DIR,filename)
fs.read() ----> 将上传的内容转成二进制方式
B. 上传到云端(对象存储)
本地的资源有限或者是空间是有限的
https://developer.qiniu.com/kodo/sdk/1242/python ---》参照python SDK
util.py:
def upload_qiniu():
#需要填写你的 Access Key 和 Secret Key
access_key = 'Access_Key'
secret_key = 'Secret_Key'
#构建鉴权对象
q = Auth(access_key, secret_key)
#要上传的空间
bucket_name = 'Bucket_Name'
#上传后保存的文件名
key = 'my-python-logo.png'
#生成上传 Token,可以指定过期时间等
token = q.upload_token(bucket_name, key, 3600)
#要上传文件的本地路径
localfile = './sync/bbb.jpg'
ret, info = put_file(token, key, localfile)
return ret,info
---->put_data() 适用于从filestorage里面读取数据实现上传
---->put_file() 指定文件路径上传
def delete_qiniu():
pass
32 分页
分页:
print(pagination.items) # [<Article 4>, <Article 3>, <Article 2>]
print(pagination.page) # 当前的页码数
print(pagination.prev_num) # 当前页的前一个页码数
print(pagination.next_num) # 当前页的后一页的页码数
print(pagination.has_next) # True
print(pagination.has_prev) # True
print(pagination.pages) # 总共有几页
print(pagination.total) # 总的记录条数
33 Nginx
1.特点:
nginx:
1。轻量级
2。并发能力强
3。高度模块化
4。负载均衡
5。反向代理
2.指令:
nginx -t 不运行,仅测试配置文件
nginx -c configpath 从指定路径加载配置文件
nginx -t -c configpath 测试指定配置文件
****启动Nginx
nginx [ -c configpath] 默认配置目录:/etc/nginx/nginx.conf
信息查看
nginx -v
nginx -V
查看进程:
ps -ef |grep nginx
****控制Nginx
nginx -s signal
stop 快速关闭
quit 优雅的关闭
reload 重新加载配置
通过系统管理
systemctl status nginx 查看nginx状态
systemctl start nginx 启动nginx服务
systemctl stop nginx 关闭nginx服务
systemctl enable nginx 设置开机自启
systemctl disable nginx 禁止开机自启
34 使用缓存:
1. Cache对象
from flask-caching import Cache
cache = Cache()
2.
config = {
'CACHE_TYPE': 'redis',
'CACHE_REDIS_HOST': '127.0.0.1',
'CACHE_REDIS_PASSWORD': 961202,
'CACHE_REDIS_PORT': 6379
}
def create_app():
.....
cache.init_app(app,config)
3. 设置缓存:
cache.set(key,value,timeout=second) ----> flask_cache_pic_abc
cache.set_many([(key,value),(key,value),(key,value),...])
获取缓存值:
cache.get(key) --->value
cache.get_many(key1,key2,...)
删除缓存:
cache.delete(key)
cache.delete_many(key1,key2,...)
cache.clear()
视图函数缓存:
@app.route("/")
@cache.cached(timeout=50)
def index():
return render_template('index.html')
35 uwsgi
uwsgi : web服务器,多线程处理的不错
1. pip install uwsgi
2. 工程目录下创建 uwsgi.ini 配置文件
3. 书写配置信息
4. 使用uwsgi服务器(推荐写绝对路径)
- 启动 uwsgi --ini uwsgi.ini
- 停止 uwsgi --stop uwsgi.pid
#--------------
#列出已安装的包
pip freeze or pip list
#导出requirements.txt
pip freeze > <目录>/requirements.txt
pip install -r requirements.txt
#卸载包
pip uninstall -r requirements.txt
#国内pypi镜像
V2EX:pypi.v2ex.com/simple
豆瓣:http://pypi.douban.com/simple
中国科学技术大学:http://pypi.mirrors.ustc.edu.cn/simple/
#指定单次安装源
pip install <包名> -i http://pypi.v2ex.com/simple
#指定全局安装源
在unix和macos,配置文件为:$HOME/.pip/pip.conf
在windows上,配置文件为:%HOME%\pip\pip.ini
[global]
timeout = 6000
index-url = http://pypi.douban.com/simple
36 flask-WTF
https://flask-wtf.readthedocs.io/en/stable/#
https://wtforms.readthedocs.io/en/2.3.x/
wtform
flask-wtf:集成了wtform,csrf的保护和文件上传功能,图形验证码。
###使用:
1。安装:
pip3 install Flask-WTF
全局使用csrf保护,
csrf = CSRFProtect(app=app)
必须需要设置SECRET_KEY这个配置项
app.config['SECRET_KEY'] = 'fgfhdf4564'
2。定义form.py:
在文件中中添加:
class UserForm(FlaskForm):
name = StringField('name', validators=[DataRequired()])
password =PasswordField('password',validators=[DataRequired(),Length(min=3,max=9,message='密码最小3位最大9位')])
recaptcha = StringField(label='验证码')
各种:Field类型
StringField
PasswordField
IntegerField
DecimalField
FloatField
BooleanField
RadioField
SelectField
DatetimeField
各种的验证(验证器):
验证器函数 说明
DataRequired/Required 代表内容是必填项 DataRequired(message='用户信息不能为空')
Email 验证电子邮件地址,要求正则模式 : ^.+@([^.@][^@]+)$
EqualTo 比较两个字段的值,常用于确认密码情况 EqualTo('要确认字段名',message='xxx')
IPAddress 验证IPV4网络地址 参数默认ipv4=True,ipv6=False
Length 验证输入字符串的长度 参数类型是字符串Length(min=6,max=30,message='个人信息的简介6-30个字')
NumberRange 验证输入的值在数字范围内
NumberRange(min=6,max=90,message='年龄为6-90岁')
Optional 无输入值时跳过其他验证函数
Regexp 使用正则表达式验证输入值 参数regex='正则模式'
URL 验证 URL URL(message='请输入正确的url地址')]
正则模式是^[a-z]+://(?P<host>[^/:]+)(?P<port>:[0-9]+)?(?P<path>\/.*)?$
AnyOf 确保输入值在可选值列表中
NoneOf 确保输入值不在可选值列表中
*****自定义验证器:
class UserForm(FlaskForm):
name = StringField('name', validators=[DataRequired()])
def validate_name(self, data):
# print('----->', type(self.name))
# print('=====>', type(data)) # <class 'wtforms.fields.core.StringField'>
if self.name.data[0].isdigit(): # data[0]是取用户名的第一个字母
raise ValidationError('用户名不能以数字开头')
def validate_recaptcha(self, data):
input_code = data.data
code = session.get('valid')
if input_code.lower() != code.lower():
raise ValidationError('验证码错误')
def validate_phone(self,data):
input_phone = data.data
# print('----####---->',input_phone) #18438613151
pattern = r'^1\d{9}1$'
res = re.match(pattern,input_phone)
if not res:
raise ValidationError('电话号码必须以一开始且结尾')
3.使用:
视图中:
.....
form =UserForm()
return render_template('user.html',form=form)
模板中:
<form action='' method=''>
# *****注意: 1. form.validate_on_submit() 只有在数据正确 并且验证csrf通过时 返回True
{{form.csrf_token}} # 不可少,负责无法提交
{{form.name}}
{{form.password}}
<input type='submit' value=''/>
</form>
4. 提交验证:
@app.route('/',methods=['GET','POST'])
def hello_world():
uform = UserForm()
if uform.validate_on_submit(): ------->主要通过validate_on_submit进行校验
name=uform.name.data
password=uform.password.data
phone=uform.phone.data
print(name,password,phone)
return '提交成功!'
return render_template('user.html', uform=uform)
##文件上传:
1。定义form
class UserForm(FlaskForm):
。。。。。。
# 上传使用的就是FileField,如果需要指定上传文件的类型需要使用:FileAllowed
icon = FileField(label='用户头像', validators=[FileRequired(), FileAllowed(['jpg', 'png', 'gif'], message='必须是图片文件格式')])
2。模板中的使用同其他类型的字段,但是必须在form上面:enctype="multipart/form-data"
3。视图函数中如果验证成功,通过:
icon = uform.icon.data -----》icon是FileStorage类型
filename = secure_filename(icon.filename)
icon.save(os.path.join(UPLOAD_DIR, filename))
37 bootstrap和wtf
1.app.py:
# form与bootstrap结合使用
@app.route('/user', methods=['GET', 'POST'])
def boot_form_user():
uform = MyForm()
return render_template('boot.html',uform=uform)
2.index.html:
{% extends "bootstrap/base.html" %}
{% import "bootstrap/wtf.html" as wtf%}
{% block styles %}
{{ super() }}
{% endblock %}
{% block content %}
<form action="{{ url_for('submit') }}" method="post" enctype="multipart/form-data">
{{ wtf.quick_form(uform, button_map={'submit_button': 'primary'},form_type='horizontal',horizontal_columns=('lg',2,5)) }}
</form>
{% endblock %}
38 Flash消息
(5) Flash消息
Flask的核心特性Flash实现: 为了解决用户提交了有一项错误的登录表单后,服务器发回的响应重新渲染了登录表单,并在表单上面显示一个消息,提示用户用户名或密码错误。常常用于表单信息提示.
flash() 作用是每次调用flash()都会触发一个消息所以可能有多个消息在排队等待显示。
get_flashed_messages() 函数获取传入的消息 其实就是一个列表, 获取的消息在下次调用时不会再次返回,因此 Flash 消息只显示一次,然后就消失了
###使用
from flask import flask,get_flashed_messages
@app.route('/register/',method=['GET','POST'])
def register():
form = Register()
if form.validate_on_submit():
flash('注册成功')
return redirect(url_for('index'))
return render_template('form.html',form=form)
###在模板中获取
#写法一:
{% for message in get_flashed_messages() %}
<div class="alert alert-success alert-dismissible" role="alert">
<strong>success!</strong>{{ message }}
</div>
{% endfor %}
#写法二:
{% with messages = get_flashed_messages(with_categories=True) %}
# messages 包含category和message
{% if messages %}
<ul>
{% for category, message in messages %}
<li class="{{ category }}">{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
####
flash使用当然不仅仅限于单一消息的传递,flash的源码定义为 :
def flash(message, category='message'):
因此可以看到不仅能传递消息message还能将传递的消息进行类别category选择
get_flashed_messages同样有接受信息类别的属性with_categories=设置为True
def get_flashed_messages(with_categories=False, category_filter=[]):
####例如:
if content.split('\n') <= 2:
flash(message=u'警告',category='warning')
elif content.split('\n') >= 5:
flash(message=u'不允许的操作',category='danger')
{% block content %}
{% for category,message in get_flashed_message(with_category=True) %}
<div class="alert alert-{{ category }} alert-dismissable">
<button type="button" class="close" data-dismiss="alert">&tims;</button>
{{ message }}
</div>
{% endfor %}
{% endblock %}
1. 闪现:
记住:
1>在一个请求结束的时候添加flash
flash('恭喜!验证成功啦!','info')
flash('哈哈哈','error')
flash(username,'warning')
2>在当前请求中渲染获取或者仅仅下一个请求中可以获取。
添加闪现:(后面的类型是可选择的)
flash('恭喜!验证成功啦!','info')
flash('哈哈哈','error')
flash(username,'warning')
获取闪现内容:
get_flashed_messages(with_categories=[True/False])
get_flashed_messages(category_filter=["error"]) 可选的
有针对性的获取对应类型的闪现消息
39 日志
日志:
1.使用app自带,
app.logger.info('')
app.logger.debug('A value for debugging')
app.logger.warning('A warning occurred (%d apples)', 42)
app.logger.error('An error occurred')
2.通过logging进行创建:
import logging
logger = logging.getLogger('name') # 默认flask的名字叫:app
logger = logging.getLogger('app')
保存到文件:
1。logging.basicConfig(filename='log.txt', filemode='a', level=logging.WARNING,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
2。logger.setLevel(level=logging.INFO)
handler = logging.FileHandler("log1.txt")
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
3。使用logger.info('message')
40 flask-restful-1
什么是RESTful架构:
(1)每一个URI代表一种资源;
(2)客户端和服务器之间,传递这种资源的某种表现层;
(3)客户端通过四个HTTP动词(GET,POST,PUT,DELETE,[PATCH]),对服务器端资源进行操作,实现"表现层状态转化"。
REST 是面向资源的,这个概念非常重要,而资源是通过 URI 进行暴露。
REST 系统的特征:
1.客户-服务器(Client-Server),提供服务的服务器和使用服务的客户需要被隔离对待。
2.无状态(Stateless),来自客户的每一个请求必须包含服务器处理该请求所需的所有信息。换句话说,服务器端不能存储来自某个客户的某个请求中的信息,并在该客户的其他请求中使用。
3.可缓存(Cachable),服务器必须让客户知道请求是否可以被缓存。(Ross:更详细解释请参考 理解本真的REST架构风格 以及 StackOverflow 的这个问题 中对缓存的解释。)
4.分层系统(Layered System),服务器和客户之间的通信必须被这样标准化:允许服务器和客户之间的中间层(Ross:代理,网关等)可以代替服务器对客户的请求进行回应,而且这些对客户来说不需要特别支持。
5.统一接口(Uniform Interface),客户和服务器之间通信的方法必须是统一化的。(Ross:GET,POST,PUT.DELETE, etc)
6.支持按需代码(Code-On-Demand,可选),服务器可以提供一些代码或者脚本(Ross:Javascrpt,flash,etc)并在客户的运行环境中执行。这条准则是这些准则中唯一不必必须满足的一条。(Ross:比如客户可以在客户端下载脚本生成密码访问服务器。)
前后端分离:
前端: app,小程序,pc页面
后端: 没有页面,mtv: 模型模板视图 去掉了t模板。
mv:模型 视图
模型的使用:跟原来的用法相同
视图: api构建视图
步骤:
1. pip3 install flask-restful
2.创建api对象
api = Api(app=app)
api = Api(app=蓝图对象)
3.定义类视图:
from flask_restful import Resource
class xxxApi(Resource):
def get(self,uid):
pass
def post(self):
pass
def put(self):
pass
def delete(self):
pass
4. 添加路由
api.add_resource(xxxApi,'/user')
api.add_resource(xxxApi,'/user/<int:uid>')
api.add_resource(xxxApi,'/goods',endpoint='goods')
api.add_resource(xxxApi,'/goods',)
参照:http://www.pythondoc.com/Flask-RESTful/quickstart.html
https://flask-restful.readthedocs.io/en/latest/
41 restful-2
Request Parsing():
进:当浏览器提交数据时,服务器要验证数据
----------------进:请求参数传入-------------------
步骤:
1。创建RequestParser对象:
# 参数解析
parser = reqparse.RequestParser(bundle_errors=True) # 获得解析对象,bundle_errors 显示所有错误信息(help)
2。给解析器添加参数:
通过parser.add_argument('名字',type=类型,required=是否必须填写,help=错误的提示信息,location=表明获取的位置form就是post表单提交)
注意在type的位置可以添加一些正则的验证等。type:str|int|inputs.regex()|FileStorage|...
例如:
parser.add_argument('username', type=str, required=True, help='必须输入用户名', location=['form'])
parser.add_argument('password', type=inputs.regex(r'^\d{6,12}$'), required=True, help='必须输入6~12位数字密码',
location=['form'])
parser.add_argument('phone', type=inputs.regex(r'^1[356789]\d{9}$'), location=['form'], help='手机号码格式错误')
parser.add_argument('hobby', action='append') # ['篮球', '游戏', '旅游']
parser.add_argument('icon', type=FileStorage, location=['files'])
只要添加上面的内容,就可以控制客户端的提交,以及提交的格式。
3。在请求的函数中获取数据:
可以在get,post,put等中获取数据,通过parser对象.parse_args()
# 获取数据
args = parser.parse_args()
args是一个字典底层的结构中,因此我们获取具体的数据时可以通过get
username = args.get('username')
password = args.get('password')
42 restful-3
------------输出-----------------
1.需要定义字典,字典的格式就是给客户端看的格式
user_fields = {
'id': fields.Integer,
'username': fields.String(default='匿名'),
'pwd': fields.String(attribute='password'),
'udatetime': fields.DateTime(dt_format='rfc822')
}
客户端能看到的是: id,username,pwd,udatetime这四个key
默认key的名字是跟model中的模型属性名一致,如果不想让前端看到命名,则可以修改
但是必须结合attribute='模型的字段名'
自定义fields
1。必须继承Raw
2。重写方法:
def format(self):
return 结果
# 自定义:
class IsDelete(fields.Raw):
def format(self, value):
print('------------------>', value) # value值为iddelete字段的值
return '删除' if value else '未删除'
user_fields = {
。。。
'isDelete1': IsDelete(attribute='isdelete'), # attribute='isdelete' 必须和数据库对应
。。。
}
URI:
xxxlist ----->点击具体的一个获取详情 ------> 详情
定义两个user_fields,
1.用于获取用户的列表信息结构的fields:
user_fields_1 = {
'id': fields.Integer,
'username': fields.String(default='匿名'),
'uri': fields.Url(endpoint='single_user', absolute=True) ---》参数使用的就是endpoint的值
}
2。具体用户信息展示的fields
user_fields = {
'id': fields.Integer,
'username': fields.String(default='匿名'),
'pwd': fields.String(attribute='password'),
'isDelete': fields.Boolean(attribute='isdelete'),
'isDelete1': IsDelete(attribute='isdelete'),
'udatetime': fields.DateTime(dt_format='rfc822')
}
涉及endpoint的定义:
api.add_resource(UserSimpleResource, '/user/<int:id>', endpoint='single_user')
出:
return data
注意:data必须是符合json格式
{
"username": "shali",
"nums": 1,
"friends": [
{
"id": 3,
"username": "bom",
"password": "123456",
"phone": "13562958862",
"udatetime": "Tue, 09 Jun 2020 20:42:04 -0000",
"isDelete": "已注销"
}
]
}
如果直接返回,不能有自定义的对象User,Friend,。。。。
如果有这种对象,需要:marchal(),marchal_with()帮助进行转换。
1。marchal(对象,对象的fields格式) # 对象的fields格式是指字典的输出格式
marchal([对象,对象],对象的fields格式)
2。marchal_with() 作为装饰器修饰请求方法
@marshal_with(user_friend_fields)
def get(self, id):
。。。。
return data
函数需要参数,参数就是最终数据输出的格式
参数: user_friend_fields,类型是:dict类型
例如:
user_friend_fields = {
'username': fields.String,
'nums': fields.Integer,
'friends': fields.List(fields.Nested(user_fields))
}
fields.Nested(fields.String) ----> ['aaa','bbb','bbbc']
fields.Nested(user_fields) -----> user_fields是一个字典结构,将里面的每一个对象转成user_fields
-----》[user,user,user]
参考面试题:https://www.cnblogs.com/Utopia-Clint/p/10824238.html
43 跨域问题
一、跨域问题来源于JavaScript的"同源策略",即只有 协议+主机名+端口号 (如存在)相同,
则允许相互访问。也就是说JavaScript只能访问和操作自己域下的资源,
不能访问和操作其他域下的资源。跨域问题是针对JS和ajax的,html本身没有跨域问题。
后端:
1。使用第三方扩展(推荐)
flask-cors
from flask_cors import CORS
cors= CORS()
与app进行绑定
cors.init_app(app=app,supports_credentials=True) # supports_credentials 令牌
2。response = make_response()
response.headers['Access-Control-Allow-Origin']='*'
response.headers['Access-Control-Allow-Methods']='GET,POST'
response.headers['Access-Control-Allow-Headers']='x-request-with,Content-type'
return response
44 restful-蓝图
二、蓝图与api使用:
user_bp = Blueprint('user', __name__)
api = Api(user_bp)
class xxxxApi(Resource):
pass
api.add_resource(xxxxApi,'/xxxx')
45 练习api
手机验证码:
需要 Redis cache
pip install redis
pip install flask-caching
sms_parser = reqparse.RequestParser()
sms_parser.add_argument('mobile', type=inputs.regex(r'^1[356789]\d{9}$'), help='手机号码格式错误', required=True,location=['form', 'args'])
# 发送手机验证码类视图
class SendMessageApi(Resource):
def post(self):
args = sms_parser.parse_args()
mobile = args.get('mobile')
ret, code = send_duanxin(mobile)
# 验证是否发送成功
if ret is not None:
if ret["code"] == 200:
# cache.set(key,value,timeout=second)
cache.set(mobile, code, timeout=1800)
return jsonify(code=200, msg='短信发送成功!')
else:
print("ERROR: ret.code=%s,msg=%s" % (ret['code'], ret['msg']))
return jsonify(code=400, msg='短信发送失败!')
#copy() 继承
lr_parser = sms_parser.copy()
lr_parser.add_argument('code', type=inputs.regex(r'^\d{4}$'), help='必须输入四位数字验证码', required=True, location='form')
本文详细介绍了Flask框架的各个方面,从环境搭建、配置、请求响应到路由、视图函数、模板渲染、数据库操作、会话机制、文件上传、分页、API开发等,深入浅出地讲解了Flask的使用和项目构建。同时涵盖了Nginx、uwsgi、缓存、日志等部署和优化知识。

被折叠的 条评论
为什么被折叠?



