1,URL与函数映射—传递参数以及参数类型
一个 URL 要与执行函数进行映射,使用的是 @app.route 装饰器。 @app.route 装饰器中,
可以指定 URL 的规则来进行更加详细的映射。
比如现在要映射一个文章详情的 URL ,文章详情的 URL 是 /article/id/ ,id有可能为1、2、3...,
那么可以通过以下方式:
@app.route('/article/<id>/')
def article_detail(id):
return '您请求的文章是:%s'% id
其中 <id> ,尖括号是固定写法,语法为 <variable> , variable 默认的数据类型是字符串。
如果需要指定类型,则要写成 <converter:variable> ,其中 converter 就是类型名称,可以有以下几种:
1. string:如果没有指定具体的数据类型,那么默认就是使用`string`数据类型。
2. int:数据类型只能传递`int`类型。
3. float:数据类型只能传递`float`类型。
4. path:数据类型和`string`有点类似,都是可以接收任意的字符串,但是`path`可以接收路径,也就是说可以包含斜杠。
5. uuid:数据类型只能接收符合`uuid`的字符串。`uuid`是一个全宇宙都唯一的字符串,一般可以用来作为表的主键。
6. any:数据类型可以在一个`url`中指定多个路径。例如:
# /blog/<id>/
# /user/<id>/
@app.route('/<any(user,blog):url_path>/<id>')
def detail(url_path,id):
if url_path=='blog':
return "博客详情:%s" %id
else :
return "用户详情:%s" %id
2,URL传递参数的两种方式
URL传递参数的两种方式:
两种方式传递参数
第一种:/路径/参数,(就是将参数嵌入到路径中),就是上面讲的。
第二种:/路径?参数名1=参数值1&参数名2=参数值2...,如:
http://127.0.0.1:5000/test/?wd=python&ie=ok
from flask import Flask,request
@app.route('/test/')
def test():
wd = request.args.get('wd')
ie = request.args.get('ie')
print('ie:',ie)
return '您通过查询字符串的方式传递的参数是:%s' % wd
额外补充:如果是 post 方法,则可以通过 request.form.get('id') 来进行获取。
使用总结:
如果你的这个页面的想要做`SEO`优化,就是被搜索引擎搜索到,那么推荐使用第一种形式(path的形式)。
如果不在乎搜索引擎优化,那么就可以使用第二种(查询字符串的形式)。
示例代码如下:
"""
url和视图函数映射
url传递参数问题
两种方式传递参数
第一种:/路径/参数,(就是将参数嵌入到路径中),就是上面讲的。
第二种:/路径?参数名1=参数值1&参数名2=参数值2...,如:
"""
from flask import Flask,request,render_template
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
@app.route('/article/<id>/') #没有指定参数的数据类型 默认参数的数据类型是string
def list1(id):
#思路
#获取id号 OK
print(id)
#拿id号 去操作数据库 得到数据库返回来的数据 no
#把数据按照一定的格式 返回给客户端浏览器 ok
return '您请求的文章详情id为:%s'%id
@app.route('/article2/<int:id>/') #指定参数的数据类型为int 以后只能匹配int类型作为参数 的url
def list2(id):
#思路
#获取id号 OK
print(id)
#拿id号 去操作数据库 得到数据库返回来的数据 no
#把数据按照一定的格式 返回给客户端浏览器 ok
return '您请求的文章详情id为:%s'%id
@app.route('/article3/<float:id>/') #指定参数的数据类型为float 以后只能匹配float类型作为参数 的url
def list3(id):
#思路
#获取id号 OK
print(id)
#拿id号 去操作数据库 得到数据库返回来的数据 no
#把数据按照一定的格式 返回给客户端浏览器 ok
return '您请求的文章详情id为:%s'%id
@app.route('/article4/<path:id>/') #指定参数的数据类型为path 以后只能匹配path类型作为参数 的url
def list4(id):
#思路
#获取id号 OK
print(id)
#拿id号 去操作数据库 得到数据库返回来的数据 no
#把数据按照一定的格式 返回给客户端浏览器 ok
return '您请求的文章详情id为:%s'%id
@app.route('/article5/<uuid:id>/') #指定参数的数据类型为uuid 以后只能匹配uuid类型作为参数 的url
def list5(id):
#思路
#获取id号 OK
print(id)
#拿id号 去操作数据库 得到数据库返回来的数据 no
#把数据按照一定的格式 返回给客户端浏览器 ok
return '您请求的文章详情id为:%s'%id
import uuid
print(uuid.uuid4())
#any:数据类型可以在一个`url`中指定多个路径。
@app.route('/<any(user,blog):module>/<int:id>/') #指定参数的数据类型为any 以后只能匹配any类型作为参数 的url
def list6(module,id):
if module == 'blog':
return "博客详情:%s" % id
else:
return "用户详情:%s" % id
#第二种:/路径?参数名1=参数值1&参数名2=参数值2...,如:
# @app.route('/list7') #这种写法只支持get请求方式 不支持post请求方式
@app.route('/list7',methods=['GET','POST']) #这种写法即支持get请求方式 又支持post请求方式
def list7():
if request.method =='GET':
uname = request.args.get('uname')
pwd = request.args.get('pwd')
# return "接收到的参数为:%s, %s"%(uname,pwd)
return render_template('login.html')
elif request.method =='POST':
uname = request.form.get('uname')
pwd = request.form['pwd']
return "POST方式接收到的参数为:%s,%s"%(uname,pwd)
if __name__ == '__main__':
app.run(debug=True)
3,url_for函数使用详解
一般我们通过一个 URL 就可以执行到某一个函数。如果反过来,我们知道一个函数,怎么去获得
这个 URL 呢? url_for 函数就可以帮我们实现这个功能。
url_for() :函数接收两个及以上的参数,他接收函数名作为第一个参数,接收对应URL规则的命名参数,如果还出现其他的参数,则会添加到 URL 的后面作为查询参数。
如:
@app.route('/post/list/<page>/')
def my_list(page):
return 'my list'
@app.route('/')
def hello_world():
# 构建出来的url:/post/my_list/2?num=8
return url_for('my_list',page=2,num=8)
# return "/post/list/2?num=8"
为什么选择`url_for` 而不选择直接在代码中拼 URL 的原因有两点:
1. 将来如果修改了 URL ,但没有修改该 URL 对应的函数名,就不用到处去替换 URL 了。
2. url_for() 函数会转义一些特殊字符和 unicode 字符串,这些事情 url_for 会自动的帮我们
搞定。
如:
@app.route('/login/')
def login():
return 'login'
@app.route('/')
def hello_world():
return url_for('login', next='/')
# /login/?next=/
# 会自动的将/编码,不需要手动去处理。
# url=/login/?next=%2F
示例代码如下:
"""
URL 匹配 视图函数
开发过程中,有时候需要拿 视图函数 去构建URL,引入url_for()函数来实现
视图函数 构建 URL
url_for() :
函数接收两个及以上的参数,他接收【函数名】作为第一个参数,
接收对应URL规则的命名参数,
如果还出现其他的参数,则会添加到 URL 的后面作为查询参数(?参数)。
"""
from flask import Flask,url_for
app = Flask(__name__)
@app.route('/')
def hello_world():
# return 'Hello World!'
# 返回页面 一个指定的url 如:“/list/”
# return '/list/'
#使用url_for()函数 构建url 如:“/list/”
#使用url_for()函数 构建 url的好处1:将来如果修改了URL ,但没有修改该 URL 对应的函数名,
就不用到处去替换 URL 了(路径变化的概率要高于函数名变化)
# return url_for('list1') #*****
#构建 url如:/list2/3/
# return url_for('list2',page=3) #*****
# 构建 url如:/list2/4/?num=8&pwd=123 了解
# return url_for('list2',page=4,num=8,pwd=123)
# 使用url_for()函数 构建 url的好处2:url_for() 函数会转义一些特殊字符和 unicode字符串,
这些事情 url_for会自动的帮我们搞定。
# return '/ms/list/?next=/'
return url_for('list1',next='/')
@app.route('/ms/list/')
def list1():
print("这是列表页")
@app.route('/list2/<page>/')
def list2(page):
print("这是列表页2,%s"%page)
if __name__ == '__main__':
app.run(debug=True)
4,URL参数类型底层原理和自定义URL转化器步骤
自定义URL转换器
自定义URL转换器的步骤:
转换器是一个类,且必须继承自werkzeug.routing.BaseConverter。
1. 实现一个类,继承自`BaseConverter`。
2. 在自定义的类中,重写`regex`,也就是这个变量的正则表达式。
3. 将自定义的类,映射到`app.url_map.converters`上。理解为加入字典DEFAULT_CONVERTERS中
比如:
app.url_map.converters['tel']=TelephoneConveter
注意:to_python方法和to_url方法的作用
1.在转换器类中,实现to_python(self,value)方法,这个方法的返回值,将会传递到 view函数中作为参数。
2.在转换器类中,实现to_url(self,values)方法,这个方法的返回值,将会在调用url_for函数的时候生成符合要求的URL形式。
5,自定义URL转换器之to_python方法和to_url方法
from flask import Flask,url_for
from werkzeug.routing import BaseConverter
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
#问题:Flask项目中 【底层】是如何实现 参数类型 格式判断的呢
#从 werkzeug.routing 导入BaseConverter类 了解 底层
# int 路径参数底层是调用 IntegerConverter类来作格式判断的
# float 路径参数底层是调用 FloatConverter类来作格式判断的
# string 路径参数底层是调用 UnicodeConverter类来作格式判断的
# ......
@app.route('/user/<int:id>/')
def profile(id):
return '你要查看的个人信息为:%s'%id
#需求1:自定义转换器 希望路径中 能匹配一个电话号码 类型的参数
# 1. 实现一个类,继承自`BaseConverter`。
# 2. 在自定义的类中,重写`regex`,也就是这个变量的正则表达式。
# 3. 将自定义的类,映射到`app.url_map.converters`上。理解为加入字典DEFAULT_CONVERTERS中
class TelephoneConveter(BaseConverter):
regex = r"1[345789]\d{9}"
app.url_map.converters['tel']=TelephoneConveter
#使用自定义转换器 实现需求
@app.route('/telephone/<tel:pnum>/')
def my_telephone(pnum):
return "您请求过来的电话号码值为:%s"%pnum
#需求2:查询多个模块的数据
#传统的思路实现
@app.route('/news_list/<modules>/')
def news_list(modules):
#modules是路径参数
print(modules)
#http://127.0.0.1:5000/news_list/hots+enter/
#需要对modules进行拆分
lm = modules.split('+')
print(lm)
print(lm[0])
print(lm[1])
#拆分后需要去数据库 做查询操作 select * from news where nmodule= 'hots' ornmoudle= 'enter'
return '你要查询的模块是:%s'%lm
class LiConverter(BaseConverter):
# 1.在转换器类中,实现to_python(self,value)方法,这个方法的返回值,将会传递到view函数中作为参数。
def to_python(self, value):
# return "hello"
# return value #value是自己传递过来的参数
return value.split('+') #可以对value进行加工后再返回
# 2.在转换器类中,实现to_url(self,values)方法,这个方法的返回值,将会在调用url_for函数的时候生成符合要求的URL形式。
def to_url(self, value):
# return "hello"
#['hots','enter']---->hots+enter
return "+".join(value)
app.url_map.converters['li']=LiConverter
#需求2:查询多个模块的数据
#自定义转换器来实现
@app.route('/news_list2/<li:modules2>/')
def news_list2(modules2):
print(modules2)
print(modules2[0])
print(modules2[1])
#此时参数已经进行了拆分 select * from news where nmodule= 'hots' ornmoudle= 'enter'
return '你要查询的模块是:%s'%modules2
@app.route('/hello/')
def hello2():
#构建 url :/news_list2/hots+enter/
args = url_for('news_list2',modules2=['hots','enter'])
return '构建出url并返回:%s'%args
if __name__ == '__main__':
app.run(debug=True)
6,flask开发必会的细节知识
在局域网中让其他电脑访问我的网站:
如果想在同一个局域网下的其他电脑访问自己电脑上的Flask网站,
那么可以设置`host='0.0.0.0'`才能访问得到。如: app.run(host='0.0.0.0')
指定端口号:
Flask项目,默认使用`5000`端口。如果想更换端口,那么可以设置`port=9000`。如:
app.run(host='0.0.0.0',port=9000)
url唯一:
在定义url的时候,一定要记得在最后加一个斜杠。
1. 如果不加斜杠,那么在浏览器中访问这个url的时候,如果最后加了斜杠,那么就访问不到。这样用户体验不太好。
2. 搜索引擎会将不加斜杠的和加斜杠的视为两个不同的url。而其实加和不加斜杠的都是同一个url,
3. 那么就会给搜索引擎造成一个误解。加了斜杠,就不会出现没有斜杠的情况。
`GET`请求和`POST`请求:
在网络请求中有许多请求方式,比如:GET、POST、DELETE、PUT请求等。那么最常用的就是`GET`和`POST`请求了。
4. `GET`请求:只会在服务器上获取资源,不会更改服务器的状态。这种请求方式推荐使用`GET`请求。
5. `POST`请求:会给服务器提交一些数据或者文件。一般POST请求是会对服务器的状态产生影响,
那么这种请求推荐使用POST请求。
6. 关于参数传递:
`GET`请求:把参数放到`url`中,通过`?xx=xxx`的形式传递的。
因为会把参数放到url中,一眼就能看到你传递给服务器的参数。这样不太安全。
`POST`请求:把参数放到`Form Data`中。会把参数放到`Form Data`中,避免了被偷瞄的风险,
但是如果别人想要偷看你的密码,那么其实可以通过抓包的形式。因为POST请求可以提交一些数据给服务器,
比如可以发送文件,那么这就增加了很大的风险。所以POST请求,对于那些有经验的黑客来讲,其实是更不安全的。
7. 在`Flask`中,`route`方法,默认将只能使用`GET`的方式请求这个url,如果想要设置自己的请求方式,
那么应该传递一个`methods`参数。
如:
@app.route('/login/',methods=['GET','POST'])
def login():
if request.method =='GET':
return render_template('login.html')
else:
return "success"
7,页面跳转和重定向
页面跳转和重定向
重定向分为永久性重定向和暂时性重定向,在页面上体现的操作就是浏览器会从一个页面自动跳转到另外一个页面。
比如用户访问了一个需要权限的页面,但是该用户当前并没有登录,因此我们应
该给他重定向到登录页面。
永久性重定向: http 的状态码是 301 ,多用于旧网址被废弃了要转到一个新的网址确保用
户的访问,最经典的就是京东网站,你输入 www.jingdong.com 的时候,会被重定向
到 www.jd.com ,因为 jingdong.com 这个网址已经被废弃了,被改成 jd.com ,所以这种情况
下应该用永久重定向。
暂时性重定向:http 的状态码是 302 ,表示页面的暂时性跳转。比如访问一个需要权限的
网址,如果当前用户没有登录,应该重定向到登录页面,这种情况下,应该用暂时性重定向。
用淘宝网演示为例进行讲解。
flask中重定向:重定向是通过 redirect(location,code=302) 这个函数来实现的,
location 表示需要重定向到的 URL ,应该配合之前讲的 url_for() 函数来使用,
code 表示采用哪个重定向,默认是 302 也即 暂时性重定向 ,可以修改成 301 来实现永久性重定向。
例如:
from flask import Flask,request,url_for,redirect
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
@app.route('/login/')
def login():
return '这是登录页面'
#falsk中重定向
@app.route('/profile/')
def proflie():
if request.args.get('name'):
return '个人中心页面'
else:
# return redirect(url_for('login'))
return redirect(url_for('login'),code=302)
if __name__ == '__main__':
app.run(debug=True)
from flask import Flask,request,redirect,url_for
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
@app.route('/login/')
def login():
return '这是登录页面'
#这是一个需要有登录权限 的功能函数
@app.route('/profile/')
def profile():
uname = request.args.get('uname')
#如果已经登录 去查看个人信息即可
if uname and uname =='momo':
return "这是个人信息中心"
#若没有 登录 则跳转到登录页面 给出提示信息
else:
# return redirect('/login/')
# return redirect('/login/',code=302)
# return redirect('/login/',code=301)
return redirect(url_for('login'),code=302)
if __name__ == '__main__':
app.run(debug=True)
8,视图函数Response返回值类型和自定义Response子类步骤
视图函数Response返回值详解
关于响应(Response)
视图函数中可以返回以下类型的值:
1.Response 对象及其子类对象
2.字符串。其实 Flask 是根据返回的字符串类型,重新创建一个 werkzeug.wrappers.Response 对象,
Response 将该字符串作为主体,状态码为200, MIME 类型为 text/html ,然后返回该 Response 对象
3.元组。元组中格式是 (response,status,headers) 。 response 为一个字符串, status 值是
状态码, headers 是一些响应头。即(响应体,状态码,头部信息),也不一定三个都要写,写两个也是可以的。
4.如果不是以上三种类型。即如果视图函数返回的数据,不是字符串,也不是元组,也不是Response对象,
就会将返回值传给` Response.force_type(rv,request.environ) `然后再将`force_type`的返回值返回给前端。
自定义的`Response`对象的步骤
1. 继承自`Response`类。
2. 实现方法`force_type(cls,rv,environ=None)`。
3. 指定`app.response_class`为你自定义的`Response`对象。
如:自定义的`Response`对象将视图中返回的字典,转换成json对象,然后返回
class JSONResponse(Response):
@classmethod
def force_type(cls, response, environ=None):
"""
这个方法只有视图函数返回 非字符串 非Response对象 非元组时才会调用
response:视图函数的返回值
"""
if isinstance(response,dict):
response = jsonify(response)
#调用父类方法
return super(JSONResponse, cls).force_type(response,environ)
app.response_class = JSONResponse
"""
视图函数中可以返回以下类型的值
1.Response 对象及其子类对象
2.字符串 (其实 底层把字符串作为主体 先封装成为Response对象后 再返回的)
3.元组 (有格式的)
4.如果不是以上三种类型。即如果视图函数返回的数据,不是字符串,也不是元组,也不是Response对象,
那么就会将返回值传给` Response.force_type(rv,request.environ) `,
然后再将`force_type`的返回值返回给前端。
"""
from flask import Flask,Response,jsonify
import json
# from werkzeug.wrappers import Response
#flask的结构 = werkzeug + jinja +click +sqlalchemy
#从flask 和 从 “werkzeug.wrappers” 导入的Response 是同一个类
app = Flask(__name__)
#返回【字符串】类型的数据 (其实 底层把字符串作为主体 先封装成为Response对象后 再返回的)
@app.route('/')
def hello_world():
# 返回的内容
content="莫莫"
return '<h3>%s</h3>'%content
#返回【元组 (有格式的)】类型的数据
#元组中格式是 (response,status,headers) 。
response 为一个字符串, status 值是状态码, headers 是一些响应头。
# 即(响应体,状态码,头部信息),也不一定三个都要写,写两个也是可以的。
@app.route('/list1/')
def list1():
# return ("露露",200,) 也不一定三个都要写,写两个也是可以的。
return ("探探",200,{'sid':'110110','sex':'1'})
#返回【Response 对象及其子类对象】类型的数据
@app.route('/list2/')
def list2():
resp = Response("返回的是一个Response对象")
#返回【Response 对象及其子类对象】的好处: 可以对resp对象进行加工后返回
resp.set_cookie('uname',"momo")
resp.set_cookie('pwd',"momo123")
return resp
#需求:希望将一个字典类型的数据 变成JSON对象返回给客户端
#问题:视图函数不支持 返回字典类型的数据
#解决:自定义`Response`的子类对象来解决
# 1. 继承自`Response`类。
# 2. 实现方法`force_type(cls,rv,environ=None)`。
# 3. 指定`app.response_class`为你自定义的`Response`对象。
class JSONResponse(Response):
@classmethod
def force_type(cls, response, environ=None):
"""
这个方法只有视图函数返回 非字符串 非Response对象 非元组时才会调用
response:视图函数的返回值
"""
if isinstance(response,dict):
#满足类型 是字典的时候 再对其做加工操作
res = json.dumps(response)
print(res)
#json.dumps(response)作用:将【字典类型的数据】变成 一个【长得像JSON对象的格式字符串】
#希望将一个字典类型的数据 变成JSON对象
resp=jsonify(response) #resp 是一个JSON对象
# 调用父类方法
return super(JSONResponse, cls).force_type(resp)
app.response_class=JSONResponse
@app.route("/myprofile/")
def profile():
return {'uname':'momo','gender':'男','school':'sxt'}
if __name__ == '__main__':
app.run(debug=True)