类视图
1-类和视图
之前我们接触的视图都是函数,所有我们一般简称视图函数。其实视图也可以基于类来实现,类视图的好处是支持继承,但是类视图不能跟函数视图一样,写完类视图还需要通过app.add_rulue(url_rule,view_func)
来进行注册。以下将对两个类视图进行讲解:
标准类视图
标准类视图是继承自flask.views.View
,并且在子类中必须实现dispatch_request
方法,这个方法类似于视图函数,也要返回一个基于Response
或者其子类的对象。
# -*- coding: utf-8 -*-
# @Time : 2020/4/16 14:32
# @Author : 大数据小J
from flask import Flask, url_for, views
app = Flask(__name__)
@app.route('/')
def hello_world():
# 如果add_url_rule给了endpoint,相当于给url起了一个新的名字,就不能访问函数名
print(url_for('core')) # 函数名
print(url_for('View')) # 当创建标准类视图的时候,可以从as_view中通过name属性来读取到值
return 'Hell world'
def index():
return '个人中心'
# 当继承标准的类视图之后,dispatch_request这个方法必须重写
class ListView(views.View):
# 该方法一定要重写
def dispatch_request(self):
# self.demo()
# 当类中可以通过方法来进行调用
return self.demo()
def demo(self):
return 'demo'
# rule必须传 rule 代表着路由的规则
# add_url_rule为新的传递路由的一个规则
app.add_url_rule('/index/', endpoint='core', view_func=index)
app.add_url_rule('/views/', view_func=ListView.as_view('View'))
if __name__ == '__main__':
app.run(debug=True)
类重写的方式来返回json
# -*- coding: utf-8 -*-
# @Time : 2020/4/16 15:25
# @Author : 大数据小J
from flask import Flask, views, jsonify
app = Flask(__name__)
# 创建一个标准类视图
class Jsonview(views.View):
# 用类的方式重写get_response,当类继承的时候,不重写之个方法就会主动抛出异常
def get_response(self):
raise NotImplementedError
def dispatch_request(self):
response = self.get_response()
return jsonify(response)
class Listview(Jsonview):
def get_response(self):
return {'username': 'SmallJ'}
# 添加url路由的规则
app.add_url_rule('/', view_func=Listview.as_view('json_data'))
if __name__ == '__main__':
app.run(debug=True, port=8787)
通过公共类来实现参数传递
# -*- coding: utf-8 -*-
# @Time : 2020/4/16 15:52
# @Author : 大数据小J
from flask import Flask, views, render_template
app = Flask(__name__)
# 把公共的功能放在一个类中进行继承处理
# 该类为公共继承
class public_views(views.View):
def __init__(self):
self.context = {
'username': 'SmallJ'
}
super(public_views, self).__init__()
# 注册页面
class register(public_views):
def dispatch_request(self):
return render_template('register.html', **self.context)
# 登录页面
class Sign_in(public_views):
def dispatch_request(self):
return render_template('Sign_in.html', **self.context)
# URL规则
app.add_url_rule('/register/', view_func=register.as_view('register'))
app.add_url_rule('/Sign_in/', view_func=Sign_in.as_view('Sign_in'))
if __name__ == '__main__':
app.run(debug=True, port=5555)
基于调度方法的视图
Flask还为我们提供了另外一种类视图flask.views.MethodView
,对每个HTTP方法执行不同的函数(映射到对应方法的小写的同名方法上)
-
基于方法的类视图,是根据请求的
method
来执行不同的方法的。 -
如果用户是发送的
get
请求,那么将会执行这个类的get
方法。 -
如果用户发送的是
post
请求,那么将会执行这个类的post
方法。 -
其他的method类似,比如
delete
,put
-
这种方式可以让代码更加简洁
-
所有和get请求相关的代码都放到
get
方法中,所有和POST
请求相关代码都放在POST
方法中。 -
就不需要通过
requests.method == 'GET'
。from flask import Flask, render_template, views, request app = Flask(__name__) class LoginView(views.MethodView): def __render(self, error=None): return render_template('login.html', error=error) def get(self): return self.__render() def post(self): username = request.form.get('username') password = request.form.get('password') return '登录成功' if username == 'SmallJ' and password == '123' else self.__render(error='用户名或密码错误') app.add_url_rule('/login/', view_func=LoginView.as_view('login')) if __name__ == '__main__': app.run(debug=True, por t=6789)
POST请求结果
GET请求结果
2-蓝图
之前我们写的url和视图函数都是处在同一个文件,如果项目比较大的话,这显然表示一个合理的结构,而蓝图可以优雅的帮我们实现这种需求,在开发项目中,我们经常会使用到蓝图来进行管理系统。
简单来说,Blueprint
是一个存储视图方法的容器,这些操作在这个Blueprint
被注册到一个应用之后就可以被调用,Flask可以通过Blueprint
来组织URL以及处理请求。
Flask使用Blueprint
让应用实现模块化,在Flask中,Blueprint具有如下属性:
- 一个项目可以具有多个
Blueprint
- 可以将一个
Blueprint
注册到任何一个未使用的的URL比如"/"、"/sample/" 或者子域名 - 在一个应用中,一个模块可以注册多次
Blueprint
可以单独具有自己的模板、静态文件或者其它的通过操作方法,它并不是必须要实现应用的视图和函数
使用蓝图的好处
方便代码的维护,开发项目进行分工合作。
蓝图的运行机制
- 蓝图是保存了一组将来可以应用对象上执行的操作,注册路由就是一种操作
- 当在app对象上调用route装饰器注册路由时,这个操作将修改对象的
url_map
路由表 - 当我们在蓝图上调用
route装饰器
注册路由时,它只是在内部的一个延迟操作记录列表defered_functions
中添加一个项 - 当执行app对象的
register_blueprint()
方法时,应用对象将从蓝图对象的defered_functions
列表中取出每一项,并以自身作为参数执行该匿名函数。
蓝图URL前缀
当我们在应用对象上注册一个蓝图时,可以指定一个url_prefix
关键字参数(这个参数默认是“/”)
# -*- coding: utf-8 -*-
# @Time : 2020/4/17 14:56
# @Author : 大数据小J
from flask import Flask
from blueprints.news import new_bp
app = Flask(__name__)
# 注册蓝图
app.register_blueprint(new_bp)
@app.route('/')
def hello_world():
return '首页'
if __name__ == '__main__':
app.run(debug=True,port=4567)
# -*- coding: utf-8 -*-
# @Time : 2020/4/17 15:00
# @Author : 大数据小J
from flask import Blueprint
# Blueprint中默认要传递两个参数
# name :为蓝图的名字
# import_name : __name__ 当前.py文件名字
new_bp = Blueprint('news', __name__)
@new_bp.route('/new/')
def news():
return '这是新闻页'
flask_blue.py
# -*- coding: utf-8 -*-
# @Time : 2020/4/17 14:56
# @Author : 大数据小J
from flask import Flask
from blueprints.news import new_bp
from blueprints.movies import movie_bp
app = Flask(__name__)
# 注册蓝图
app.register_blueprint(new_bp)
app.register_blueprint(movie_bp)
@app.route('/')
def hello_world():
return '首页'
if __name__ == '__main__':
app.run(debug=True, port=4567)
news.py
# -*- coding: utf-8 -*-
# @Time : 2020/4/17 15:00
# @Author : 大数据小J
from flask import Blueprint
# Blueprint中默认要传递两个参数
# name :
# import_name : 为当前的文件夹的名字
# url_prefix : 为动态URL前缀
new_bp = Blueprint('news', __name__, url_prefix='/new/')
@new_bp.route('/')
def news():
return '这是新闻页'
@new_bp.route('/detail/<int:bid>/')
def new_page(bid):
return '这是新闻的第 %d 页' % bid
movies.py
# -*- coding: utf-8 -*-
# @Time : 2020/4/17 15:01
# @Author : 大数据小J
from flask import Blueprint
movie_bp = Blueprint('movie', __name__, url_prefix='/movie/')
@movie_bp.route('/')
def movie():
return '这是电影首页'
@movie_bp.route('/page/<int:aid>/')
def movie_page(aid):
return '这是电影的第 %d 页' % aid
设置蓝图中模板的目录
蓝图对象默认的模板目录为系统的模板目录,可以在创建蓝图对象时使用template_folder
关键字参数设置模板目录
- 当
template_folder
存在,会在传递了指定文件目录的时候,会去当前目录下寻找该模板文件 - 当
template_folder
不存在的话,会去templates
文件夹目录下寻找该相对应的模板文件 - 注意:如果在
templates
中存在和lgion
文件中有相同的.html
文件的话,系统会优先级寻找templates
中的html
# -*- coding: utf-8 -*-
# @Time : 2020/4/17 15:00
# @Author : 大数据小J
from flask import Blueprint, render_template
# Blueprint中默认要传递两个参数
# name :
# import_name : 为当前的文件夹的名字
# url_prefix : 为动态URL前缀
new_bp = Blueprint('news', __name__, url_prefix='/new/', template_folder='lgion')
@new_bp.route('/')
def news():
# return '这是新闻页'
return render_template('index.html')
@new_bp.route('/detail/<int:bid>/')
def new_page(bid):
return '这是新闻的第 %d 页' % bid
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>这是蓝图中的模板文件</h1>
</body>
</html>
注册蓝图中的静态文件的相关路由
-
和应用对象不同,蓝图对象创建时不会默认注册静态目录的路由。需要我们在创建时指定
static_folder
参数。 -
默认不设置任何静态文件路径,
Jinja2
会在项目的static
文件夹中寻找静态文件,也可以设置其他的路径,在初始化蓝图的时候,Blueprint
这个构造函数,有一个参数staric_folder
可以指定静态文件的路径。 -
static_folderk
可以是相对路径(相对蓝图文件所在的目录),也可以是绝对路径。在配置完蓝图后,还有一个需要注意的地方是如何在模板中引用静态文件。在模板中引用蓝图,应该要使用蓝图名+.+static
来引用
news.py
# -*- coding: utf-8 -*-
# @Time : 2020/4/17 15:00
# @Author : 大数据小J
from flask import Blueprint, render_template
# Blueprint中默认要传递两个参数
# name :
# import_name : 为当前的文件夹的名字
# url_prefix : 为动态URL前缀
new_bp = Blueprint('news', __name__, url_prefix='/new/', template_folder='lgion', static_folder='static')
@new_bp.route('/')
def news():
# return '这是新闻页'
return render_template('index.html')
@new_bp.route('/detail/<int:bid>/')
def new_page(bid):
return '这是新闻的第 %d 页' % bid
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="{{ url_for('news.static', filename='new.css') }}">
</head>
<body>
<h1>这是蓝图中的模板文件</h1>
</body>
</html>
寻找静态文件
跟静态文件一样,默认不设置任何模块文件路径,将会在项目的templates
中寻找模板文件。也可以设置其他的路径,在构建函数Blueprint
中有一个template_folder
参数可以设置模板的路径
bp = Blueprint('news', __name__, url_prefix='/new/', template_folder='lgion', static_folder='static')
模板文件和静态文件有点区别,以上代码写完以后,如果你渲染应该模板return render_template('[...].html')
,Flask默认会去项目根目录下的templates
文件夹中查找该.html
文件,如果找到了就直接返回,如果没有找到,才会去蓝图文件所在的目录下的templates
文件夹中寻找。
url_for生成url
用url_for
生成蓝图的url
,使用的格式是:蓝图的名称+视图函数名称
。
3- 子域名
什么是子域名
子域名,类似于xxx.douban.com的形式,如book.douban.com, music.douban.com, movie.douban.com等
用Flask怎么实现子域名
flask在注册路由的时候,允许一个参数subdomain
子域名在许多网站中都用到了,比如一个网站叫做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
上出现。