1.flask视图和URL
1.第一个flask程序
from flask import Flask ''' Flask这个类是项目的核心,以后很多操作都是基于这个类的对象 注册URL等等,都是基于这个类 ''' app = Flask(__name__) ''' 用Flask类创建一个对象,传递__name__参数进入 这里__name__参数的作用: 1.可以规定模板和静态文件的查找路径 2.以后flask的一些插件如果报错,通过__name__可以找到具体的错误位置 关于这里的__name__后面会详细介绍 ''' @app.route(r"/") def hello(): return "hello" ''' @app.route是一个装饰器,里面传入URL,会将该URL映射到所装饰的函数上面 当访问"/"的时候,会执行hello函数,从而返回hello 个人觉得:flask设计的真是太优雅了,路由,视图非常直观。 ''' if __name__ == '__main__': app.run() ''' 运行app.run方法,也就是启动这个网站 关于app.run,这是flask的一个测试应用服务器,仅在测试使用,不推荐部署时使用 那么它的原理是什么呢?就类似于一个while 死循环,里面有一个listen方法,不断地监听。 '''
执行成功
但是我们如果不想监听5000端口,该怎么办呢?直接run方法里面加上相应的参数即可
app.run(host="localhost", port=8000)
2.debug模式详解
from flask import Flask app = Flask(__name__) @app.route(r"/") def satori(): ''' 如果这直接返回的话肯定是没问题的,但是如果当中出现了错误该怎么办呢? 比方说,我写一个1/0 ''' 1/0 return "<h1>hello satori</h1>" if __name__ == '__main__': app.run(host="localhost", port=8000)
if __name__ == '__main__': app.run(host="localhost", port=8000, debug=True)
如果加上了debug=True的话,那么结果就不同了
再来看看报错提示
加上之后则给予了非常明显的提示
为什么需要开启debug模式
1.如果开启了debug模式,那么在代码中如果抛出了异常,在浏览器中可看到具体的错误信息和错误代码位置。方便开发者调试
2.如果开启了debug模式,那么以后修改了flask代码,无序手动重启,会自动重启,类似于Django。
配置debug模式的四种方式
1.上面已经出现过了,通过app.run(debug=True)来设置
2.app=Flask(__name__);app.debug = True,也可以开启debug模式
app = Flask(__name__) app.debug = True
3.app下面有一个config相当于是app的配置文件,是一个字典。也可以通过app.config.update(DEBUG=True)或者app.config.update({"DEBUG": True})或者app.config["DEBUG"]=True来设置,注意这里的DEBUG要大写.
app = Flask(__name__) app.config["DEBUG"] = True ''' 或者app.config.update("DEBUG"=True) 或者app.config.update({"DEBUG": True}) '''
4.同过配置文件的形式设置,将DEBUG=True写在一个配置文件里面,比如config.py,然后导入config,通过app.config.from_object(config)设置debug模式
config.py
DEBUG = True
import config app = Flask(__name__) app.config.from_object(config)
3.配置文件的两种方式
###使用`app.config.from_object的方式加载配置文件`
1. import config
2. app.config.from_object(config)
###使用`app.config.from_pyfile的方式加载配置文件
1. app.config.from_pyfile("config.py")
上一种方式属于加载模板,需要先导入config。但是这种方式属于加载文件,因此只需要传入文件名即可,因此后缀名.py不可省略
''' 而且app.config.from_pyfile,不仅可以加载py文件,其他文件也可以。 app.config.from_pyfile("config.py", silent=False),还可以有一个silent参数。 如果为False,表示如果文件没找到就报错,为True表示如果没找到就什么也不做,程序继续往下走。 '''
4.url中传递参数
from flask import Flask app = Flask(__name__) # 只需要访问localhost:8000即可 @app.route(r"/") def hello(): return "<h1>hello cruel world</h1>" # 访问localhost:8000/satori即可,访问该路由会自动执行该路由对应的装饰器所装饰的函数 # 从而返回hello satori # 再提一句,flask的api设计的真优雅 @app.route(r"/satori") def satori(): return "hello satori" # 如何指定参数呢? # 在tornado中使用(),在flask中要使用<>,并且要指定名字 @app.route(r"/mashiro/<age>/<anime>") def mashiro(anime, age): ''' 路由中指定名字,那么在视图函数中也要定义相同个数的同名参数,否则报错 那么当我访问localhost:8000/mashiro/18/樱花庄 的时候,age就等于18, anime就等于樱花庄 因此在执行视图函数的时候,18就会传给视图函数里的age,樱花庄就会传给anime,最终在页面上显示。 当然这里视图函数的参数顺序无所谓,因为我们相当于指定了变量名 等价于: localhost:8000/mashiro/18/樱花庄 --映射--> /mashiro/<age>/<anime> ===>age=18, anime="樱花庄" def mashiro(anime, age): return age, anime mashiro(age=18, anime="樱花庄") 因此视图函数里的参数顺序不重要,因为路由里的参数和视图里的参数保持一致,传参使用类似于关键字传参的方式 ''' return f"age={age}, anime={anime}" if __name__ == '__main__': app.run(host="localhost", port=8000)
# 现在问题又来了,如果我想限制格式怎么办? # 比如这里,这里的length显然要求的是int类型,如果输入的不是int,那么不进行匹配 # 在tornado中,可以使用正则r"/96猫@/\d+/cm"这种形式,但在flask中怎么做呢? # 直接使用表示类型的关键字即可,<type:var>,比如这里就可以写r"/96猫@/<int:length>/cm # 注意不要写成int: length, :左右两边不能有空格 @app.route(r"/96猫@/<int:length>/cm") def neko(length): return f"96猫@{length}cm"
# 那么关于类型的限制都可以输入哪些类型呢? ''' string:/默认的数据类型,就是说我们不加类型,默认是string,可以接收没有任何斜杠"\/"的文本 int:接收整型 float:浮点型 path:和string类似,但是可以接收斜杠。 uuid:接收uuid字符串 any:可以指定多种路径 ''' @app.route(r"/koishi/<path:name>") def koishi(name): return name @app.route(r"/matsuri/<uuid:uuid>") def uuid(uuid): return str(uuid) # 这里的any要说一下,上面的r"/matsuri/<uuid:uuid>表示我们访问localhost:8000/matsuri/uuid类型 # 而这里的any(foo, bar),表示我们输入的必须是localhost:8000/foo或者localhost:8000/bar # 就是说<int:age>,我们必须输入int类型,然后这个int交给age,<any(foo, bar):func>,表示必须输入foo或者bar,然后交给func,看个栗子 @app.route(r"/any/<any(foo, bar):func>") def ccc(func): if func == "foo": return "foo" else: return "bar"
# 如果我们想要拿到用户输入的参数该怎么办呢? # tornado的话,直接是self.get_argument(),在flask里面也类似 # 首先from flask import request,然后request.args.get()即可 @app.route(r"/args") def get_argument(): # 所有的参数和值都会以键值对的形式存储在request.args里 # 这个request.args是一个ImmutableMultiDict,说明可以使用dict的api return f"a={request.args.get('a')}, b={request.args.get('b')}, c={request.args.get('c')}"
5.url_for
from flask import Flask ''' 下面介绍一下flask的url_for,这是个什么东西。如果知道tornado,那么肯定知道在tornado里面有一个反响解析,self.reverse_url 这个tornado的反响解析比较类似,既然要用,那么肯定要先导入进来 ''' from flask import url_for ''' 函数的原型:def url_for(endpoint, **values): 这里的endpoint是什么?首先我们在浏览器上面输入的请求最终都是有我们的视图函数来执行的 因此endpoint就是我们的函数,但是传参的时候,以字符串的形式。 下面举个栗子: ''' app = Flask(__name__) @app.route("/hello") def hello_world(): return "hello cruel world" @app.route("/get") def get_url(): # 看到这里不用想也应该明白这个link在执行的时候,会等于什么? # url_for表示获取视图函数所对应的路由,那么link显然就是/hello link = url_for("hello_world") return f'<h1><a href="{link}">还是去看hello world吧</a></h1>' if __name__ == '__main__': app.run(host='localhost', port=8888)
@app.route("/hello/<adj>") def hello_world(adj): return f"hello {adj} world" @app.route("/get") def get_url(): # 不过这里还有一个隐藏的问题,那就是如果hello_world里面需要参数呢? # 比方说这里的cruel太残忍,世界有时还是美好的。所以我们不想把返回值写死,而是自由指定,该怎么办呢? # 直接在后面加上adj="xxx",即可 # 那么link就会等于/hello/adj,这里的adj就是beautiful link = url_for("hello_world", adj="beautiful") return f'<h1><a href="{link}">去看看吧,世界变得美好了</a></h1>'
@app.route("/hello/<adj>") def hello_world(adj): return f"hello {adj} world" @app.route("/get") def get_url(): # 解决了传参的问题之后,又出现了新的问题,就是这样,问题往往就是递归出现的 # 在hello_world中只需要接收一个参数,但是如果我们多指定两个呢? link = url_for("hello_world", adj="beautiful", i="666", you="mmp") return f'<h1><a href="{link}">{link}</a></h1>'
url_for总结:第一个参数应该是视图函数名对应的字符串,后面的参数传递给url。如果传递的参数在url中已经定义了,那么这个参数就会被当成path的形式传递给url。如果参数在url中没有定义,那么将变成查询字符串的形式放到url中。
6.自定义url转换器
之前我们使用了int,path等等定义url,但如果我们想要自己指定url的格式该怎么办呢?
首先我们要知道在werkzeug.routing里面有一个BaseConverter,我们把模块的注释去掉拿出来看看
''' 可以看到其他所有的Converter都是集成这个BaseConverter ''' class BaseConverter(object): """Base class for all converters.""" regex = '[^/]+' weight = 100 def __init__(self, map): self.map = map def to_python(self, value): return value def to_url(self, value): return url_quote(value, charset=self.map.charset) class UnicodeConverter(BaseConverter): def __init__(self, map, minlength=1, maxlength=None, length=None): BaseConverter.__init__(self, map) if length is not None: length = '{%d}' % int(length) else: if maxlength is None: maxlength = '' else: maxlength = int(maxlength) length = '{%s,%s}' % ( int(minlength), maxlength ) self.regex = '[^/]' + length class AnyConverter(BaseConverter): def __init__(self, map, *items): BaseConverter.__init__(self, map) self.regex = '(?:%s)' % '|'.join([re.escape(x) for x in items]) class PathConverter(BaseConverter): regex = '[^/].*?' weight = 200 DEFAULT_CONVERTERS = { 'default': UnicodeConverter, # 因此不加转换器默认是string类型 'string': UnicodeConverter, 'any': AnyConverter, 'path': PathConverter, 'int': IntegerConverter, 'float': FloatConverter, 'uuid': UUIDConverter, }
因此我们也可以按照相同的方式,定义一个类,继承自BaseConverter
from flask import Flask from werkzeug.routing import BaseConverter app = Flask(__name__) class InfoConverter(BaseConverter): ''' 我们定义一个转换器,要求输入身高体重。 格式:xxxcm@xxxkg ''' # 定义一个正则 regex = r"\d{2,3}cm@\d{1,3}kg" # 然后要讲转换器进行注册, 这样才能找得到 # app.url_map.converters是一个字典 ''' self.converters = self.default_converters.copy() default_converters = ImmutableDict(DEFAULT_CONVERTERS) DEFAULT_CONVERTERS = { 'default': UnicodeConverter, 'string': UnicodeConverter, 'any': AnyConverter, 'path': PathConverter, 'int': IntegerConverter, 'float': FloatConverter, 'uuid': UUIDConverter, } ''' # 直接添加进去即可, 然后就可以直接使用了。info便也成了我们的转换器 app.url_map.converters["info"] = InfoConverter # 因此info要满足InfoConverter里面定义的正则,满足regex,r"\d{2,3}cm@\d{1,3}kg" @app.route(r"/satori/<info:info>") def satori(info): return f"success,info = {info}" if __name__ == '__main__': app.run(host="localhost", port=8888)
自定义url转换器的方式
1.实现一个类,类继承自`BaseConverter`
2.在自定义的类中,重写`regex`,也就是变量的正则表达式
3.将自定义的类映射到app.url_map.converters里面
下面介绍一下to_python
to_python也是定义在转换器里面
class ListConverter(BaseConverter): # 同样需要集成BaseConverter def to_python(self, value): # 会将to_python的返回值传递到view中作为参数 return value.split("+") app.url_map.converters["list"] = ListConverter @app.route(r"/mashiro/<list:value>") def mashiro(value): ''' 当我们在浏览器中输入之后,会得到value的值。 一般情况下,value会自动传进视图函数,但是我们用list转换器。 那么会将value传进to_python,然后将to_python的返回值传给value。 因此转换器可以有两种:第一种是输入的格式有限制,但是不会改变,从浏览器中得到的什么就往视图函数里面传什么 第二种是输入的格式没有限制,但是会将得到的内容进行转化,然后再传给视图函数。 to_python显然是第二种 ''' return f"value = {value}"
除了to_python之外,还有一个to_url。是干什么的呢?主要和url_for一起使用,不好说明,直接看栗子吧
class ListConverter(BaseConverter): def to_python(self, value): return value.split("+") def to_url(self, value): return "hello" app.url_map.converters["list"] = ListConverter @app.route(r"/mashiro/<list:value>") def mashiro(value): return "hello world" @app.route(r"/koishi") def koishi(): # 在获取视图函数mashiro对应的路由时,也会将to_url的返回值加到后面。因为mashiro函数中有一个list:value,list转换器中定义了to_url link = url_for("mashiro", value="随+便+指+定+一+个") return link
class ListConverter(BaseConverter): def to_python(self, value): return value.split("+") # 所以获取有list转换器的路由,会自动把这个to_url的返回值加在后面 # 所以在返回值为hello的情况下,/mashiro/<list:value>获取到的就是/mashiro/hello # 那么这个value是干啥的,刚才不说我们要获取的是有list转换器的路由吗?那list装饰的value怎么办呢? # 其实list装饰的value,在url_for中指定的value,以及这里的value是一个值。 # 首先没有指定list装饰的话,那么会变成什么样子? # 我在url_for中指定的value(就叫 随+便+指+定+一+个),会自动将/mashiro/<value>变成/mahiro/随+便+指+定+一+个 # 但是这里的value被装饰了,所以就不会传给/mashiro/<list:value>,而是会传给to_url中的value # 然后再将to_url的返回值传给/mashiro/<list:value> # 这也是为什么刚才的栗子中会显示/mashiro/hello # 我们将返回值hello改成别的 def to_url(self, value): return "".join(value.split("+")) app.url_map.converters["list"] = ListConverter @app.route(r"/mashiro/<list:value>") def mashiro(value): return "hello world" @app.route(r"/koishi") def koishi(): # 在获取视图函数mashiro对应的路由时,也会将to_url的返回值加到后面 link = url_for("mashiro", value="随+便+指+定+一+个") return link
POST请求
''' 我们之前的请求都是get请求,那么如何进行post请求呢? 在app.route里面直接加上methods=["POST"]即可,不加参数默认是get请求 如果即想get又想post,那么可以methods=["GET", "POST"] ''' @app.route(r"/post", methods=["POST"]) def post(): return "success"
我们来模拟一个表单提交
1.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/info" method="post"> 姓名:<input type="text" name="username"> 密码:<input type="password" name="password"> <input type="submit" value="提交"> </form> </body> </html>
''' 我们来进行一个表单提交 flask和tornado不一样,我们如果不指定methods,那么默认只是GET 当我们输入url,会显示一个表单页面,这没问题。一旦当我们点击提交,就是POST请求。 而默认只能GET,所以就报错了。因此必须要显示的指明请求种类,加上methods=["GET", "POST"],表示两种请求都支持 ''' @app.route('/info', methods=["GET", "POST"]) def info(): # 用于获取参数 from flask import request # 用于渲染模板 from flask import render_template if request.method == "GET": ''' 模板渲染默认使用的是jinja2,而且在没设置模板路径的情况下, 要把html文件放在templates文件夹里面,不要和py文件放在一起。 因为是flask使用jinja2去找,所以要指定位置,不指定的话,默认是到templates这个文件夹下面找 ''' return render_template("1.html") # 否则执行post请求 else: ''' request.args.get,是获取get请求(url)里面的参数 request.form.get,是获取post请求(form表单)里面的参数 ''' name = request.form.get("username") passwd = request.form.get("password") return f"username is {name}, password is {passwd}"
下面介绍一下重定向
重定向分为永久性重定向和暂时性重定向,在页面的体现就是浏览器会从一个页面跳转到另一个页面。比如用户访问了一个需要权限的页面,但是该用户当前并没有登录,因此我们应该给他重定向到登录页面。
永久性重定向:http的状态码是301,多用于旧网址被废弃了要转移到新网址确保用户被访问,最经典的就是京东网站,输入www.jingdong.com,会被重定向到www.jd.com,因为www.jingdong.com这个网址已经废弃了,所以这种情况应该被永久重定向。
暂时性重定向:http状态码是302,表示页面的暂时性跳转。比如访问一个需要权限的网址,如果当前用户没有登录,应该重定向到登录页面。这种情况应该使用暂时性重定向
在flask中实现重定向可以通过flask下的redirect来实现
from flask import Flask, request from flask import redirect from flask import url_for app = Flask(__name__) @app.route(r"/login") def login(): return "欢迎登陆" @app.route(r"/index") def index(): return '<h2 style="text-align: center">欢迎来到古明地觉的避难小屋<h2>' ''' 访问/info?name=satori&password=komeijisatori,显然会重定向到index 否则会重定向到/login ''' @app.route(r"/info") def info(): name = request.args.get("name") password = request.args.get("password") if name == "satori" and password == "komeijisatori": # code为301,永久性重定向。302,暂时性重定向。默认为302 return redirect(url_for("index"), code=302) else: return redirect(url_for("login")) if __name__ == '__main__': app.run(host="localhost", port=8888)
输入localhost:8888/info?name=satori&password=komeijisatori
如果输入其他
关于响应(response)
from flask import Flask from flask import Response ''' 其实这个Response是位于werkzeug.wrappers下,但是我们通过flask也能导入 其实说白了flask+werkzeug+SQLAlchemy+jinja2是一个整体,werkzeug和jinja2都在flask下的__init__中被导入了进来 所有我们from flask import Response 和 from werkzeug.wrappers import Response是一样的 ''' app = Flask(__name__) @app.route(r"/index") def index(): ''' 其实return返回只能返回一个Response对象, 这里返回一个hello world等价于Response("hello world", status=200, mimetype="text/html") 会自动帮我们做一个转换 ''' return "hello world" @app.route(r"/index1") def index1(): ''' 也可以返回一个元组,response,status,headers(一个列表或者字典,作为额外的消息头) ''' return "hello index", 200, {"X-NAME": "satori"} if __name__ == '__main__': app.run(host="localhost", port=8888)
''' 如果以上条件都不满足,那么flask会假设返回值是合法的 然后通过Response.force_type(rv, request.environ)转化为一个请求对像 ''' # 首先字典是无法返回的,我们可以将视图函数中返回的字典转换成一个json对象,然后返回 # restful-api class JsonResponse(Response): @classmethod def force_type(cls, response, environ=None): ''' 这个方法只有在返回非Response对象,非字符,非元组才会调用 response就是视图函数的返回值,而force_type的返回值再相应给前端 ''' import json # 使用这种方法的时候,一定要加上Response return Response(json.dumps(response)) # 记得一定要指定一下,否则没有效果 app.response_class = JsonResponse @app.route(r"/blog") def blog(): return {"name": "satori"}
@app.route(r"/foo") def foo(): # 我们也可以这样写,然后返回。 # 当然这个直接返回foo是一样的,因为flask会自动帮我们执行这一步 res = Response("foo") # 但为什么还推荐这么写呢?因为如果想设置cookie的话,那么便只能加上Response res.set_cookie("foo", "bar") return res
1.flask视图和URL的更多相关文章
- Flask视图函数报fmalformed url rule错误的原因
Flask视图函数报fmalformed url rule错误,原因可能是包含中文字符了 把标点符号都重新写一遍英文格式的,可能就不会报这个了
- Flask内置URL变量转换器
Flask内置URL变量转换器: 转换器通过特定的规则执行,”<转换器: 变量名>”.<int: year>把year的值转换为证书,因此我们可以在视图函数中直接对year变量 ...
- Python+Django+SAE系列教程9-----Django的视图和URL
第三.四.五章介绍的就是Django中MVC的视图.模板.模型了. 首先来看视图(view),在用Django生成的站点目录中,创建一个view.py文件,这个文件開始是空的.然后我们输入下面内容: ...
- django-高级视图和url配置
高级视图和url配置 一.URLconf技巧 1.流线型化函数导入 对于配置url,我们可以使用以下几种方式: (1)引入view中的函数 from firstSite.view import cur ...
- 视图和URL配置
视图和URL配置 实验简介 上一章里我们介绍了如何创建一个Django项目并启动Django的开发服务器.本章你将学到用Django创建动态网页的基本知识. 同时,也教会大家怎么在本地机器上建立一个独 ...
- 视图的URL配置,找不到我设置的第一个Page
问题:视图的URL配置,找不到我设置的第一个Page 我的代码如下: 结果访问/test/时说找不到这个page 原因:patterns方法的参数有两个,一个是prefix,一个是参数元祖,详见下 ...
- Django笔记 —— 高级视图和URL配置
最近在学习Django,打算玩玩网页后台方面的东西,因为一直很好奇但却没怎么接触过.Django对我来说是一个全新的内容,思路想来也是全新的,或许并不能写得很明白,所以大家就凑合着看吧- 本篇笔记(其 ...
- Django学习day4——视图和URL配置
创建一个简单的hello world 在day3中我们第一次运行了服务器,里面是一个django的欢迎页面,那是因为我们没有配置URL和视图,django在底层会自动跳转这个页面上 我们在mysite ...
- Django---路由系统,URLconf的配置,正则表达式的说明(位置参数),分组命名(捕获关键字参数),传递额外的参数给视图,命名url和url的反向解析,url名称空间
Django---路由系统,URLconf的配置,正则表达式的说明(位置参数),分组命名(捕获关键字参数),传递额外的参数给视图,命名url和url的反向解析,url名称空间 一丶URLconf配置 ...
随机推荐
- Win7系统下删除文件时出现“正在准备再循环”的解决方法
今天,笔者在备份文件的时候,将一个word文档从移动硬盘复制到桌面.经过一系列“复(meng)杂(bi)”的操作之后,笔者突然发现,文件无法删除了.当右键文件点击“删除”时,出现对话框显示“正在准备 ...
- (转)java中equals和等号(==)的区别浅谈
java中的数据类型,可分为两类:1.基本数据类型,也称原始数据类型.byte,short,char,int,long,float,double,boolean 他们之间的比较,应用双等号(==) ...
- 判断腾讯QQ是否在线
http://webpresence.qq.com/getonline?Type=1&1617052138: 判断腾讯QQ是否在线接口. 下面是个简单的例子: <!doctype htm ...
- [CF1076E]Vasya and a Tree
题目大意:给定一棵以$1$为根的树,$m$次操作,第$i$次为对以$v_i$为根的深度小于等于$d_i$的子树的所有节点权值加$x_i$.最后输出每个节点的值 题解:可以把操作离线,每次开始遍历到一个 ...
- BZOJ 1023: [SHOI2008]cactus仙人掌图 | 在仙人掌上跑DP
题目: 求仙人掌直径 http://www.lydsy.com/JudgeOnline/problem.php?id=1023 题解: 首先给出仙人掌的定义:满足所有的边至多在一个环上的无向联通图 我 ...
- C&C++——extern
1.C 调用C++的函数或变量 C 调用C++的函数或变量,在C++的头文件声明为extern "C" ,C调用的时候只使用extern 声明. 可见,extern "C ...
- 自定义CheckBox
自定义android的CheckBox按钮图形有两个步骤三种方式: 第一步: 新建Android XML文件,类型选Drawable,根结点选selector,放置在drawable文件夹内,指定各种 ...
- 继承spring的validator接口,实现对数据的校验
在org.springframework.validation这个包中提供了一些对数据校验的方法,其中Validator接口是其中的一个. 现在用Validator接口,完成对数据的校验. 第一步:先 ...
- 单个回调函数中返回多个Request以及Item
import scrapy from myproject.items import MyItem class MySpider(scrapy.Spider): name = 'example.com' ...
- SVN 服务器安装及配置(WIN7)
软件安装包 客户端: 服务端: 安装服务端 不整合 Apache 服务器可以忽略此选项. 安装程序会自动在path下配置好环境变量:D:\Subversion\bin; 查看是否安装成功: C:\Us ...