目录
写在前面
想来好久没写Blog了,学习果然是一个痛苦的事情,坚持下去太难了,但是我们终究要面对生活。博主最近在学习Flask框架,我会把学习过程中的心得和大家分享,如有问题或者建议,欢迎评论,系列持续更新中。。。
简介
Flask是一款非常流行的微型Python Web框架,基于Werkzeug WSGI工具箱和Jinja2 模板引擎。 Flask使用BSD授权。 Flask也被称为“microframework”,因为它使用简单的核心,用extension增加其他功能。Flask没有默认使用的数据库、窗体验证工具。然而,Flask保留了扩增的弹性,可以用Flask-extension加入这些功能:ORM、窗体验证工具、文件上传、各种开放式身份验证技术。
Flask出生于2010年,作者是Armin Ronacher,本来这个项目只是作者在愚人节的一个玩笑,后来由于非常受欢迎,进而成为一个正式的项目。
Flask自2010年发布第一个版本以来,大受欢迎,深得开发者的喜爱,并且在多个公司已经得到了应用,Flask能如此流行的原因,可以分为以下几点:
- 微框架、简洁、只做他需要做的,给开发者提供了很大的扩展性。
- Flask和相应的插件写得很好,用起来很爽。
- 开发效率非常高,比如使用SQLAlchemy的ORM操作数据库可以节省开发者大量书写sql的时间
Flask的灵活度非常之高,他不会帮你做太多的决策,一些你都可以按照自己的意愿进行更改。
- 使用Flask开发数据库的时候,具体是使用SQLAlchemy还是MongoEngine,选择权完全掌握在你自己的手中。区别于Django,Django内置了非常完善和丰富的功能,并且如果你想替换成你自己想要的,要么不支持,要么非常麻烦。
- 把默认的Jinija2模板引擎替换成其他模板引擎都是非常容易的。
第一个flask程序
helloworld.py
# 从flask框架中导入Flask类
from flask import Flask
# 传入__name__初始化一个Flask实例
app = Flask(__name__)
# app.route装饰器映射URL和执行的函数。这个设置将根URL映射到了hello_world函数上
# 这个步骤也称为配置路由
@app.route('/')
# 被路由装饰的函数便是视图
def hello_World():
return 'Hello World'
if __name__ == '__main__':
# 运行本项目,host=0.0.0.0可以让其他电脑也能访问到该网站,port指定访问的端口。
# 默认的host是127.0.0.1,port为5000
app.run()
以上就是Flask框架的基本使用,点击运行,点击python console中的网页链接或者在浏览器中输入http://127.0.0.1:5000就能看到Hello World
了。需要说明一点的是,app.run这种方式只适合于开发,如果在生产环境中,应该使用Gunicorn或者uWSGI来启动。如果是在终端运行的,可以按ctrl+c来让服务停止。
Debug模式
默认情况下flask不会开启DEBUG模式,开启DEBUG模式后,flask会在每次保存代码的时候自动的重新载入代码,并且如果代码有错误,会在访问的网页进行提示,方便进行代码错误的排查。
开启DEBUG模式的四种方式
1.直接在应用对象上设置
app.debug = True
app.run()
2.在执行run方法的时候,传递参数进去
app.run(debug=True)
3.在config属性中设置
app.config.update(DEBUG=True)
4.PyCharm专业版对Flask框架有专门的支持,可在配置项目的时候设置开启
如果一切正常,会在终端打印以下信息:
* Restarting with stat
* Debugger is active!
* Debugger pin code: 294-745-044
* Running on http://0.0.0.0:9000/ (Press CTRL+C to quit)
需要注意的是,只能在开发环境下开启DEBUG模式,因为DEBUG模式会带来非常大的安全隐患。
另外,在开启了DEBUG模式后,当程序有异常而进入错误堆栈模式,你第一次点击某个堆栈想查看变量值的时候,页面会弹出一个对话框,让你输入PIN值,这个PIN值在你启动的时候就会出现,比如在刚刚启动的项目中的PIN值为294-745-044,你输入这个值后,Werkzeug会把这个PIN值作为cookie的一部分保存起来,并在8小时候过期,8小时以内不需要再输入PIN值。这样做的目的是为了更加的安全,让调试模式下的攻击者更难攻击到本站。
URL与视图
URL与函数的映射
从之前的helloworld.py文件中,我们已经看到,一个URL要与执行函数进行映射,使用的是@app.route装饰器。@app.route装饰器中,可以指定URL的规则来进行更加详细的映射,比如现在要映射一个文章详情的URL,文章详情的URL是/article/id/,id有可能为1、2、3…,那么可以通过以下方式
@app.route('/article/<id>/')
def article(id):
return '%s article detail' % id
其中,尖括号是固定写法,语法为,<converter:variable>。类似于定义变量,converter表示数据类型,variable表示变量名称。其中converter:可以省略,默认的数据类型是字符串,数据类型有以下几种:
- string: 默认的数据类型,接受没有任何斜杠/的字符串。
- int: 整形
- float: 浮点型。
- path: 和string类似,但是可以传递斜杠/。
- uuid: uuid类型的字符串。
- any:可以指定多种路径
@app.route('/<any(article,blog):url_path>/')
def item(url_path):
return url_path
如果不想定制子路径来传递参数,也可以通过传统的?=的形式来传递参数,例如:/article?id=xxx,这种情况下,可以通过request.args.get(‘id’)来获取id的值。如果是post方法,则可以通过request.form.get(‘id’)来进行获取。
构造URL(url_for)
一般我们通过一个URL就可以执行到某一个函数。如果反过来,我们知道一个函数,怎么去获得这个URL呢?url_for函数就可以帮我们实现这个功能。url_for()函数接收两个及以上的参数,他接收函数名作为第一个参数,接收对应URL规则的命名参数,如果还出现其他的参数,则会添加到URL的后面作为查询参数。
通过构建URL的方式而选择直接在代码中拼URL的原因有两点:
- 将来如果修改了URL,但没有修改该URL对应的函数名,就不用到处去替换URL了。
- url_for()函数会转义一些特殊字符和unicode字符串,这些事情url_for会自动的帮我们搞定
from flask import Flask,url_for
app = Flask(__name__)
@app.route('/article/<id>/')
def article(id):
return '%s article detail' % id
@app.route('/')
def index():
print(url_for("article",id=1))
return "首页"
# >/article/1/
URL末尾的斜杠
有些URL的末尾是有斜杠的,有些URL末尾是没有斜杠的。这其实是两个不同的URL
@app.route('/article/')
def articles():
return '文章列表页'
上述例子中,当访问一个结尾不带斜线的URL:/article,会被重定向到带斜线的URL:/article/上去。但是当我们在定义article的url的时候,如果在末尾没有加上斜杠,但是在访问的时候又加上了斜杠,这时候就会抛出一个404错误页面了:
@app.route("/article")
def articles(request):
return "文章列表页面"
以上没有在末尾加斜杠,因此在访问/article/的时候,就会抛出一个404错误。
总结:代码中路径加上/,网页可加可不加,代码中不加/,网页不能加
HTTP访问方式
HTTP访问方式主要有两种:
- get
- post
在@app.route()中可以传入一个关键字参数methods来指定本方法支持的HTTP方法,默认情况下,只能使用GET请求
@app.route('/login/',methods=['GET','POST'])
def login():
return 'login'
以上装饰器将让login的URL既能支持GET又能支持POST
页面跳转和重定向
重定向分为永久性重定向和暂时性重定向,在页面上体现的操作就是浏览器会从一个页面自动跳转到另外一个页面。比如用户访问了一个需要权限的页面,但是该用户当前并没有登录,因此我们应该给他重定向到登录页面:
- 永久性重定向:http的状态码是301,多用于旧网址被废弃了要转到一个新的网址确保用户的访问,最经典的就是京东网站,你输入www.jingdong.com的时候,会被重定向到www.jd.com,因为jingdong.com这个网址已经被废弃了,被改成jd.com,所以这种情况下应该用永久重定向。
- 暂时性重定向:http的状态码是302,表示页面的暂时性跳转。比如访问一个需要权限的网址,如果当前用户没有登录,应该重定向到登录页面,这种情况下,应该用暂时性重定向。
在flask中,重定向是通过flask.redirect(location,code=302)这个函数来实现的,location表示需要重定向到的URL,应该配合之前讲的url_for()函数来使用,code表示采用哪个重定向,默认是302也即暂时性重定向,可以修改成301来实现永久性重定向
from flask import Flask,url_for,redirect
app = Flask(__name__)
app.debug = True
@app.route('/login/',methods=['GET','POST'])
def login():
return 'login page'
@app.route('/profile/',methods=['GET','POST'])
def profile():
name = request.args.get('name')
if not name:
return redirect(url_for('login'))
else:
return name
类视图
之前我们接触的视图都是函数,所以一般简称视图函数。其实视图也可以基于类来实现,类视图的好处是支持继承,但是类视图不能跟函数视图一样,写完类视图还需要通过app.add_url_rule(url_rule,view_func)来进行注册。
标准类视图
标准类视图是继承自flask.views.View,并且在子类中必须实现dispatch_request方法,这个方法类似于视图函数,也要返回一个基于Response或者其子类的对象
from flask.views import View
class PersonalView(View):
def dispatch_request(self):
return "逻辑课堂"
# 类视图通过add_url_rule方法和url做映射
app.add_url_rule('/users/',view_func=PersonalView.as_view('personalview'))
基于调度方法的视图
Flask还为我们提供了另外一种类视图flask.views.MethodView,对每个HTTP方法执行不同的函数(映射到对应方法的小写的同名方法上)
class LoginView(views.MethodView):
# 当客户端通过get方法进行访问的时候执行的函数
def get(self):
return render_template("login.html")
# 当客户端通过post方法进行访问的时候执行的函数
def post(self):
email = request.form.get("email")
password = request.form.get("password")
if email == 'xx@qq.com' and password == '111111':
return "登录成功!"
else:
return "用户名或密码错误!"
# 通过add_url_rule添加类视图和url的映射,并且在as_view方法中指定该url的名称,方便url_for函数调用
app.add_url_rule('/myuser/',view_func=LoginView.as_view('loginview'))
用类视图的一个缺陷就是比较难用装饰器来装饰,比如有时候需要做权限验证的时候
from flask import session
def login_required(func):
def wrapper(*args,**kwargs):
if not session.get("user_id"):
return 'auth failure'
return func(*args,**kwargs)
return wrapper
装饰器写完后,可以在类视图中定义一个属性叫做decorators,然后存储装饰器。以后每次调用这个类视图的时候,就会执行这个装饰器
class UserView(views.MethodView):
decorators = [user_required]
...
蓝图
之前我们写的url
和视图函数都是处在同一个文件,如果项目比较大的话,这显然不是一个合理的结构,而蓝图可以优雅的帮我们实现这种需求。
from flask import Blueprint
bp = Blueprint('user',__name__,url_prefix='/user/')
@bp.route('/')
def index():
return "用户首页"
@bp.route('profile/')
def profile():
return "个人简介"
然后我们在主程序中,通过app.register_blueprint()
方法将这个蓝图注册进url映射中,看下主app
的实现
from flask import Flask
import user
app = Flask(__name__)
app.register_blueprint(user.bp)
if __name__ == '__main__':
app.run(host='0.0.0.0',port=9000)
以后访问/user/
,/user/profile/
,都是执行的user.py
文件中的视图函数,这样就实现了项目的模块化。
以上是对蓝图的一个简单介绍,但是使用蓝图还有几个需要注意的地方,就是在蓝图如何寻找静态文件、模板文件,url_for
函数如何反转url
寻找静态文件
默认不设置任何静态文件路径,Jinja2
会在项目的static
文件夹中寻找静态文件。也可以设置其他的路径,在初始化蓝图的时候,Blueprint
这个构造函数,有一个参数static_folder
可以指定静态文件的路径
bp = Blueprint('admin',__name__,url_prefix='/admin',static_folder='static')
static_folder
可以是相对路径(相对蓝图文件所在的目录),也可以是绝对路径。在配置完蓝图后,还有一个需要注意的地方是如何在模板中引用静态文件。在模板中引用蓝图,应该要使用蓝图名+.+static
来引用
<link href="{{ url_for('admin.static',filename='about.css') }}">
寻找模板文件
跟静态文件一样,默认不设置任何模板文件的路径,将会在项目的templates
中寻找模板文件。也可以设置其他的路径,在构造函数Blueprint
中有一个template_folder
参数可以设置模板的路径
bp = Blueprint('admin',__name__,url_prefix='/admin',template_folder='templates')
模板文件和静态文件有点区别,以上代码写完以后,如果你渲染一个模板return render_template('admin.html')
,Flask
默认会去项目根目录下的templates
文件夹中查找admin.html
文件,如果找到了就直接返回,如果没有找到,才会去蓝图文件所在的目录下的templates
文件夹中寻找。
url_for生成url
:
用url_for
生成蓝图的url
,使用的格式是:蓝图名称+.+视图函数名称
。比如要获取admin
这个蓝图下的index
视图函数的url
url_for('admin.index')
其中这个蓝图名称是在创建蓝图的时候,传入的第一个参数。
bp = Blueprint('admin',__name__,url_prefix='/admin',template_folder='templates')
子域名
子域名在许多网站中都用到了,比如一个网站叫做xxx.com
,那么我们可以定义一个子域名cms.xxx.com
来作为cms
管理系统的网址,子域名的实现一般也是通过蓝图来实现,在之前章节中,我们创建蓝图的时候添加了一个url_prefix=/user
作为url前缀,那样我们就可以通过/user/
来访问user
下的url。但使用子域名则不需要。另外,还需要配置SERVER_NAME
,
比如app.config[SERVER_NAME]='example.com:9000'
。并且在注册蓝图的时候,还需要添加一个subdomain
的参数,这个参数就是子域名的名称
from flask import Blueprint
bp = Blueprint('admin',__name__,subdomain='admin')
@bp.route('/')
def admin():
return 'Admin Page'
这个没有多大区别,接下来看主app
的实现
from flask import Flask
import admin
# 配置`SERVER_NAME`
app.config['SERVER_NAME'] = 'example.com:8000'
# 注册蓝图,指定了subdomain
app.register_blueprint(admin.bp)
if __name__ == '__main__':
app.run(host='0.0.0.0',port=8000,debug=True)
写完以上两个文件后,还是不能正常的访问admin.example.com:8000
这个子域名,因为我们没有在host
文件中添加域名解析,你可以在最后添加一行127.0.0.1 admin.example.com
,就可以访问到了。另外,子域名不能在127.0.0.1
上出现,也不能在localhost
上出现。
关于响应(Response)
视图函数中可以返回以下类型的值:
- Response对象。
- 字符串。其实Flask是根据返回的字符串类型,重新创建一个werkzeug.wrappers.Response对象,Response将该字符串作为主体,状态码为200,MIME类型为text/html,然后返回该Response对象。
- 元组。元组中格式是(response,status,headers)。response为一个字符串,status值是状态码,headers是一些响应头。
- 如果不是以上三种类型。那么Flask会通过Response.force_type(rv,request.environ)转换为一个请求对象。
直接使用Response创建
from werkzeug.wrappers import Response
@app.route('/about/')
def about():
resp = Response(response='about page',status=200,content_type='text/html;charset=utf-8')
return resp
可以使用make_response函数来创建Response对象,这个方法可以设置额外的数据,比如设置cookie,header信息
from flask import make_response
@app.route('/about/')
def about():
return make_response('about page')
通过返回元组的形式
@app.errorhandler(404)
def not_found():
return 'not found',404