Files
python-book/04.Flask/03.视图高级.md
2025-08-27 14:39:37 +08:00

183 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 视图高级
## 1. 类视图
之前我们接触的视图都是函数,所以一般简称视图函数。其实视图也可以基于类来实现,类视图的好处是支持继承,但是类视图不能跟函数视图一样,写完类视图还需要通过`app.add_url_rule(url_rule,view_func)`来进行注册。以下将对两种类视图进行讲解:
### 1.1 标准类视图:
标准类视图是继承自`flask.views.View`,并且在子类中必须实现`dispatch_request`方法,这个方法类似于视图函数,也要返回一个基于`Response`或者其子类的对象。以下将用一个例子进行讲解:
```python
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'))
```
### 1.2 基于调度方法的视图:
`Flask`还为我们提供了另外一种类视图`flask.views.MethodView`对每个HTTP方法执行不同的函数映射到对应方法的小写的同名方法上以下将用一个例子来进行讲解
```python
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'))
```
如果用类视图,我们怎么使用装饰器呢?比如有时候需要做权限验证的时候,比如看以下例子:
```python
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`,然后存储装饰器。以后每次调用这个类视图的时候,就会执行这个装饰器。示例代码如下:
```python
class UserView(views.MethodView):
decorators = [user_required]
...
```
## 2. 蓝图和子域名
### 2.1 蓝图:
之前我们写的`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`,以下分别进行解释:
#### 2.1.1 寻找静态文件:
默认不设置任何静态文件路径,`Jinja2`会在项目的`static`文件夹中寻找静态文件。也可以设置其他的路径,在初始化蓝图的时候,`Blueprint`这个构造函数,有一个参数`static_folder`可以指定静态文件的路径,如:
```python
bp = Blueprint('admin',__name__,url_prefix='/admin',static_folder='static')
```
`static_folder`可以是相对路径(相对蓝图文件所在的目录),也可以是绝对路径。在配置完蓝图后,还有一个需要注意的地方是如何在模板中引用静态文件。在模板中引用蓝图,应该要使用`蓝图名+.+static`来引用,如下所示:
```python
<link href="{{ url_for('admin.static',filename='about.css') }}">
```
#### 2.1.2 寻找模板文件:
跟静态文件一样,默认不设置任何模板文件的路径,将会在项目的`templates`中寻找模板文件。也可以设置其他的路径,在构造函数`Blueprint`中有一个`template_folder`参数可以设置模板的路径,如下所示:
```python
bp = Blueprint('admin',__name__,url_prefix='/admin',template_folder='templates')
```
模板文件和静态文件有点区别,以上代码写完以后,如果你渲染一个模板`return render_template('admin.html')``Flask`默认会去项目根目录下的`templates`文件夹中查找`admin.html`文件,如果找到了就直接返回,如果没有找到,才会去蓝图文件所在的目录下的`templates`文件夹中寻找。
#### 2.1.3 url_for生成`url`
`url_for`生成蓝图的`url`,使用的格式是:`蓝图名称+.+视图函数名称`。比如要获取`admin`这个蓝图下的`index`视图函数的`url`,应该采用以下方式:
```python
url_for('admin.index')
```
其中这个**蓝图名称**是在创建蓝图的时候,传入的第一个参数。`bp = Blueprint('admin',__name__,url_prefix='/admin',template_folder='templates')`
### 2.2 子域名:
子域名在许多网站中都用到了,比如一个网站叫做`xxx.com`,那么我们可以定义一个子域名`cms.xxx.com`来作为`cms`管理系统的网址,子域名的实现一般也是通过蓝图来实现,在之前章节中,我们创建蓝图的时候添加了一个`url_prefix=/user`作为url前缀那样我们就可以通过`/user/`来访问`user`下的url。但使用子域名则不需要。另外还需要配置`SERVER_NAME`,比如`app.config[SERVER_NAME]='example.com:9000'`。并且在注册蓝图的时候,还需要添加一个`subdomain`的参数,这个参数就是子域名的名称,先来看一下蓝图的实现(admin.py)
```python
from flask import Blueprint
bp = Blueprint('admin',__name__,subdomain='admin')
@bp.route('/')
def admin():
return 'Admin Page'
```
这个没有多大区别,接下来看主`app`的实现:
```python
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`上出现。