第三章 URL与视图
配置文件两种方式详解
先讲两种直接传参:
直接简单传参
app =Flask(__name__) app.config['DEBUG']=True
app.config.update( DEBUG=true, SECRET_KEY='xxxx' )
1.使用app.config.from_object的方式加载配置文件
- 导入import config
- 使用app.config.from_object(config)
2.使用app.config.from_pyfile的方式加载配置文件:
- 这种方式不需要import,直接使用app.config.from_pyfile('config.py')。
- 注意这个地方,必须要写文件的全名以及后缀名。
- 加载配置文件,不局限于只能使用py文件,普通的txt文件同样也适合。
- 传递silent=True,则静态文件没有找到的时候,不会抛出异常。
为什么需要url_for:
1. 将来如果修改了URL,但没有修改该URL对应的函数名,就不用到处去替换URL。
2. url_for会自动的处理那些特殊的字符,不需要手动去处理。
url = url_for('login',next='/') # 会自动的将/编码,不需要手动去处理。 # url=/login/?next=%2F
URL中两种方式传参
第一种:使用在视图函数的path的形式(将参数嵌入到路径)
第二种:使用查询字符串的方式,就是通过"?key=value"的形式传递的
如果这个页面的想要做`SEO`优化,就是被搜索引擎搜索到,那么推荐使用第一种形式(path的形式)
如果不在乎搜索引擎优化,那么就可以使用第二种(查询字符串的形式)
一个URL要与执行函数进行映射,使用的是@app.route 装饰器。app.route装饰器中,可以指定URL的规则来进行更加详细的映射,比如现在要映射一个文章详情的URL,文章详情的URL是/article/le/,id有可能为1、2、3.那么可以通过以下方式:
@app.route('/detail/<id>/') def detail(id): return 'detail'+id
其中<id>,尖括号是固定写法,语法为<variable-name),variable_name 里默认的数据类型是字符串。如果需要指定类型,则要写成<converter:variable-name,其中converter就是类型名称,可以有以下几种:
- ·string:默认的数据类型,接受没有任何斜杠\ or /的文本。
- ·int:接受整形。
- ·float:接受浮点类型。
- ·path:和string的类似,但是接受料杠。
- -uuid:只接受uuid字符串。
- ·any:可以指定多种踏径
这个通过几个例子来进行说明:
@app.route('/<any(blog,article):url_path>/<id>/') def detail(url_path,id): if url_path == 'blog': return '博客详情:%s' % id else: return '博客详情:%s' % id
对any进行补充:有点像我们学过的枚举。在any里面枚举出所有的可能。
@app.route('/p/<float:article_id>') def article_detail(article_id): return '您请求的文章是:%s' % article_id
如果不想定制子路径来传递参数,也可以通过传统的?=的形式来传递参数,例如:/article?id=xxx,这种情况下,可以通过request.args.get("id")来获取id的值。如果是post方法,则可以通过request.form.get("id")未进行获取。
@app.route('/d/') def d(): wd = request.args.get('wd') ie = request.args.get('ie') print('ie:',ie) return '您通过查询字符串的方式传递的参数是:%s' % wd
url_for使用详解
url_for的基本使用:
- url_for第一个参数,应该是视图函数的名字的字符串。
- 后面的参数就是传递给url:
-
- 如果传递的参数之前在url中已经定义了,那么这个参数就会被当成path的形式给url。
- 如果这个参数之前没有在url中定义,那么将变成查询字符串的形式放到url中。
3.url的返回值也是字符串
@app.route('/') def hello_world(): return url_for('loginFunc',next='1') #url_for的返回值是字符串 # /login/?next=1 @app.route('/login/') def loginFunc(): return 'login界面:'
@app.route('/') def hello_world(): return url_for('loginFunc2',next='1') #url_for的返回值是字符串 # /login/1 @app.route('/login/<next>') def loginFunc2(next): print(next) return 'login--next--界面:'
自定义URL转换器
笔者认为自定转化器更准确的是自定义URL的数据类型,int、string、path没法满足业务需求。
我们先以int的数据格式去了解数据格式
@app.route('/user/<int:user_id>/') def user_profile(user_id): return '您输入的user_id为:%s' % user_id
自定义"手机号"的数据转化器
# 一个url中,含有手机号码的变量,必须限定这个变量的字符串格式满足手机号码的格式 class TelephoneConveter(BaseConverter): regex = r'1[85734]\d{9}' #必须添加到converters中 app.url_map.converters['tel'] = TelephoneConveter @app.route('/telephone/<tel:my_tel>/') def my_tel(my_tel): return '您的手机号码是:%s' % my_tel
# 用户在访问/posts/a+b/ class ListConverter(BaseConverter): def to_python(self, value): return value.split('+') def to_url(self, value): return "+".join(value) # return "hello" app.url_map.converters['list'] = ListConverter @app.route('/posts/<list:boards>/') def posts(boards): print(boards) return "您提交的板块是:%s" % boards @app.route('/') def hello_world(): print('='*30) return url_for('posts',boards=['a','b'])
页面跳转和重定向
重定向分为永久性重定向和暂时性重定向,在页面上体现的操作就是浏览器会从一个页面自动跳转到另外一个页面。比如用户访问了一个需要权限的页面,但是该用户当前并没有登录,因此我们应该给他重定向到登录页面。
·永久性重定向:
http的状态码是301,多用于旧网址被废弃了要转到一个新的网址确保用户的访问,最经典的就是京东网站,你输入www.jingoong.con的时候,会被重定向到www.jd.com,因为jngdong.con这个网址已经被废弃了,被改成30.con,所以这种情况下应该用永久重定向。
·暂时性重定向:
http的状态码量302,表示页面的暂时性跳转。比如访问一个需要权限的网址,如果当前用户没有登示,应该重定问到登录页面,这种情况下,应该用暂时性重定问。
在flask中,重定向是通过flask.redirect(location,code=302)这个函数来实现的,1ocation表示需要重定向到的URL,应该配合之前讲的ur1_for()函效来使用,code表示采用哪个重定向,默认是302也即临时性重定向,可以修改成301来实现永久性重定向。
下面是京东的网站的永久重定向:
下面是淘宝【已买到宝贝】的临时性重定向:
案例代码如下:
flask有一个函数redirect可以重定向
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(): if request.args.get('name'): return '个人中心页面' else: return redirect(url_for('login'))
视图函数Response返回值详解
关于响应(Response):
视图函数的返回值会被自动转换为一个响应对象,F1ask的转换逻辅如下:
- ·如果返回的是一个合法的响应对象,则直接返回。
- ·如果返回的是一个字符串,那么Flask会重新创建一个werkzeug.wrapper3.Response对象,Response将该字符串作为主体,状态码为200,NTE类型为text/html,然后返回该Response对象。
- ·如果返回的是一个元组,元祖中的数据类型是(response,status,headers),status值会覆盖认的200状态码,headers可以是一个列表或者字典,作为额外的消息头。
- ·如果以上条件都不满足,Flask会假设返回值是一个合法的WSGIT应用程序,并通过Response.force_type(rv,request.environ)转换为一个请求对象。
以下将用例子来进行说明:
1.return-->Response 直接使用Response创建:
from flask import Flask,Response #或者是 #from werkzeug.wrappers import Response @app.route('/') def hello_world(): res=Response('Hello Worldl1',status=200,content_type='/text/html') return res #下面的return语句其实是上面的Response的简化 # return'Hello World!'
2.可以使用make_response函数来创建Response对象,这个方法可以设置额外的数据,比如设置cookie,header信息
3.返回一个元祖
@app.route("/list2/") def list2(): return "list2",200,{"x-name":"wqbin"}
4.自定义一个Response子类去调用force_type实现返回json
note:最新的flask已经支持返回字典类型,通过调用jsonify
- ·必须继承自Response类。
- ·实现类方法force_type(cls,rv,environ=None)。
- ·必须指定app.response_class为你自定义的Response
以下将用一个例子来进行讲解,RestfulAPI都是通过json的形式进行传递,如果你的后台跟前台进行交互,所有的URL都是发送json数据,
那么此时你可以自定义一个叫做]SONResponse的类来代替Flask自带的Response类:
from flask import Flask,Response,jsonify # flask = werkzeug+sqlalchemy+jinja2 # import json 使用自带json去解析会报错 app = Flask(__name__) # 将视图函数中返回的字典,转换成json对象,然后返回 # restful-api class JSONResponse(Response): @classmethod def force_type(cls, response, environ=None): """ 这个方法只有视图函数返回非字符、非元组、非Response对象 才会调用 response:视图函数的返回值 """ if isinstance(response,dict): # jsonify除了将字典转换成json对象,还将改对象包装成了一个Response对象 response = jsonify(response) return super(JSONResponse, cls).force_type(response,environ) app.response_class = JSONResponse @app.route('/list3/') def list3(): return {'username':'wqbin','age':24} if __name__ == '__main__': app.run(debug=True,port=8000)
5.小用法设置cookie
@app.route('/list1/') def list1(): resp = Response('list1') resp.set_cookie('country','china') return resp