写在文前
养成做演练的习惯。因为我之前从来不在大众面前做演讲,这次我要主持公司的年度大会,我开始写词,从开场、串场到结束,不仅各个环节需要做好衔接,还要把整个演讲词给背诵下来。在这个过程中让我明白了凡事要做计划和演练,否则你就不会达到你所想的目标。
视图函数与URL的绑定(俗称映射)
- 添加URL与视图函数的映射。你不仅可以以使用app.route(rule, endpoint=None),还可以使用app.add_url_route(url_rule, endpoint=None, view_func=None),其实app.route()装饰器底层的实现逻辑还是app.add_url_route()。
- rule指的是你输入浏览器地址栏的URL,是字符串的格式。
- endpoint指的是视图函数的别名,如果没有指定,url_for()则默认使用视图函数的名字,否则url_for()必须使用这个endpoint的名字,url_for()中的参数字符串的格式。
- view_func指的是视图函数名字,非字符串,而是不带括号的视图函数名字。
# app.py
from flask import Flask, render_template, url_for
app = Flask(__name__)
# 配置自动加载模板文件
app.config['TEMPLATES_AUTO_RELOAD'] = True
@app.route('/')
def hello_world():
# 输出的是/home/
print(url_for("back"))
return "hello world"
@app.route("/index/")
def get_index():
return render_template('index.html')
def get_home():
return render_template("home.html")
app.add_url_rule('/home/', endpoint="back", view_func=get_home)
if __name__ == '__main__':
app.run()
标准类视图
- 我们之前接触的视图都是函数形式的,其实视图也可以基于类来进行实现,类视图可以支持继承,但是类视图函数需要通过app.add_url_rule()注册才能使用。
- 标准类视图,必须继承自flask.views.View,必须实现dispatch_request方法,每当前端的请求过来后,这个方法就会自动运行给出返回值(与之前的视图保持相同),这个返回值也是个Response对象。
- app.add_url_rule()函数中如果指定了endpoint参数,那么在使用url_for()函数反转视图的时候就必须使用endpoint参数指定的值;如果endpoint没有指定值,那么就可以使用as_view("视图名字")指定的视图名字进行反转。
# app.py
from flask import Flask, render_template, views, url_for, jsonify
app = Flask(__name__)
# 配置自动加载模板文件
app.config['TEMPLATES_AUTO_RELOAD'] = True
class JsonView(views.View):
def get_data(self):
return "data"
# 这个方法必须要实现,dispatch是指处理,这里主要是处理请求
def dispatch_request(self):
return jsonify(self.get_data())
class Index(JsonView):
def get_data(self):
return {'Navigation1': 'home', 'Navigation2': 'book'}
# 类表示实现多个处理方法的视图,
# 类调用as_view()方法把其转换为视图函数
# 传入自定义的端点值(用来生成URL),最后将它赋给view_func参数。
app.add_url_rule('/index/',
endpoint="get_index",
view_func=Index.as_view("back")
)
class AdsView(views.View):
def __init__(self):
# 如果子类继承父类不做初始化,那么会自动继承父类属性。
# 如果子类继承父类做了初始化,且不调用super初始化父类构造函数,那么子类不会自动继承父类的属性。
# 如果子类继承父类做了初始化,且调用了super初始化了父类的构造函数,那么子类也会继承父类的属性。
super(AdsView, self).__init__()
self.context = {
"variable1": "value1",
"variable2": "value2"
}
class ChargeView(AdsView):
def dispatch_request(self):
return render_template("charge.html", **self.context)
class LoginView(AdsView):
def dispatch_request(self):
self.context.update({
"variable3": "value3",
})
return render_template("login.html", **self.context)
app.add_url_rule('/charge/', view_func=ChargeView.as_view("get_charge"))
app.add_url_rule('/login/', view_func=LoginView.as_view("get_login"))
@app.route('/')
def hello_world():
# 输出:/index/
print(url_for("get_index"))
# 输出:/charge/,这个是不指定endpoint关键字参数的情况下的输出
print(url_for('get_charge'))
return "hello world"
if __name__ == '__main__':
app.run()
调度方法视图
- 基于请求方法的类视图,是根据请求的方法来执行不同的方法,如果前端发送的是get方法,那么就是执行这个类的get方法,其它类推。
- 基于请求方法的类视图,可以让代码更简洁,所有和get请求相关的代码都放在get方法中,所有和post请求相关的代码都放在类的post方法中,这样就不需要通过请求的方法来进行判断。
# app.py
from flask import Flask, views, render_template, request
app = Flask(__name__)
# 配置自动加载模板文件
app.config['TEMPLATES_AUTO_RELOAD'] = True
@app.route('/')
def hello_world():
return "hello world"
class LoginView(views.MethodView):
# __error
def __error(self, error=None):
# 必须以error=error传递error这个参数,因为前端模板有{{ error }}这个变量
return render_template('login.html', error=error)
def get(self):
return self.__error()
def post(self):
# 必须对应前端的form表单的提交方式为:method="post"
username = request.form.get('username')
password = request.form.get('password')
if username == "link" and password == "123":
return "登录成功"
else:
return self.__error(error="用户名或密码错误!")
# /login/必须加斜杠
# 否则会出现错误:ValueError: urls must start with a leading slash
app.add_url_rule('/login/', view_func=LoginView.as_view("login"))
if __name__ == '__main__':
app.run()
{#login.html#}
{% extends "common/base.html" %}
{% block title %}
Chain
{% endblock %}
{% block body_block %}
<form action="" method="post">
<table>
<tbody>
<tr>
<td>用户名:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="登录"></td>
</tr>
</tbody>
</table>
{% if error %}
<p style="color: red">{{ error }}</p>
{% endif %}
</form>
{% endblock %}
类视用装饰器
- 对视图函数使用装饰器,那么自己定义的装饰器必须在app.route装饰器下面,否则自己定义的装饰器就不会起作用。
- 在类视图中的装饰器,需要重写类视图的decorators类属性,给这个类属性赋列表或者元组,里面就是自己定义的装饰器。
# app.py
from flask import Flask, request, views
from functools import wraps
app = Flask(__name__)
# 配置自动加载模板文件
app.config['TEMPLATES_AUTO_RELOAD'] = True
# 装饰器的作用:在不改变原有功能代码的基础上,添加额外的功能,如用户验证等
# @wraps(view_func)的作用:不改变使用装饰器原有函数的结构(如__name__, __doc__)
def login_required(func):
@wraps(func)
def wrapper(*args, **kwargs):
username = request.args.get("username")
if username and username == 'chain':
# *的变量名会存放所有未命名的变量参数,并且会把位置参数转换成元组的形式
# 关键字参数**kwargs:把N个关键字参数,转换成了字典。
return func(*args, **kwargs)
else:
return "请先登录"
return wrapper
@app.route('/')
def hello_world():
return "hello world"
@app.route('/setting/')
# login_required不用加括号,其次是要放在@app.route('/setting/')的下面
# 正常前端访问该url,界面显示请先登录,如果输入
# http://127.0.0.1:5000/setting/?username=chain,则会正常的显示设置页面
@login_required
def setting():
return "设置页面"
class Information(views.View):
decorators = [login_required]
def dispatch_request(self):
return "个人中心"
app.add_url_rule('/info/', view_func=Information.as_view("info"))
if __name__ == '__main__':
app.run()
蓝图(蓝本)
- 如果项目庞大繁杂,我们就需要了解项目的组织技巧,蓝图的目的就是让Flask项目更加模块化,结构更加清晰,蓝图可以将相同的模块视图函数放在同一个蓝图下,同一个文件中,更加方便开发者管理。
- 蓝图的基本语法:首先在项目根目录下建立包文件blueprints;其次建立单个蓝图py文件;在蓝图文件中from flask import Blueprint,建立user_bp = Blueprint('admin', __name__, url_prefix="/info");最后要在app.py文件中注册蓝图。
- 如果想要在某个蓝图下的url前都要一个相同的前缀,那么在定义蓝图的时候,指定关键字参数url_prefix="/info",如果/info后面不增加/,那么在定义视图函数的时候,就不需要再增加/了。
- 蓝图中模板文件寻找规则:如果项目中的templates文件夹中有相应的模板文件可直接使用;如果templates文件夹没有相应的模板文件,那么就会到定义蓝图的指定的template_folder路径中寻找,并且在蓝图中指定的路径可以为相对路径,相对指的是当前这个蓝图文件所在的目录。
- 蓝图中静态文件寻找规则:在模板文件中,加载静态的文件,如果使用url_for(),那么就只会在app指定的静态文件加目录下查找静态文件;如果指定蓝图的名字,比如"admin.static",那么Flask就会到这个蓝图指定的static_folder下查找静态文件。
- url_for()反转蓝图中的视图函数为url:项目使用了蓝图,在模板或视图函数中使用url_for()函数的时候,必须要加上蓝图的名字.视图函数名字,否则就找不到这个endpoint。
项目结构
demo4
│
├─static
│ admin_css.css
│
├─templates
│ index.html
│
├─user
│ │ admin.py
│ │ __init__.py
│ │
│ ├─chain
│ │ │ home.html
│ │ │
│ │ └─blue_static
│ │ admin_css.css
# chain/demo4/app.py
from flask import Flask, url_for
from user.admin import user_bp
app = Flask(__name__)
app.register_blueprint(user_bp)
@app.route('/')
def hello_world():
print(url_for('users.user_info'))
return 'Hello World!'
if __name__ == '__main__':
app.run()
# demo4/user/admin.py
from flask import Blueprint, render_template
# name是为蓝本的名字,这个参数必须存在
# import_name是蓝本所在的包或模块
# 如果blue_static文件夹在chain文件下,则static_folder="chain/blue_static"
# 如果blue_static文件夹在user文件下,则static_folder="blue_static"
# users也是模板文件中的url_for('users.static', filename='admin_css.css')
user_bp = Blueprint('users', __name__,
url_prefix="/info",
template_folder="chain",
static_folder="blue_static",
)
@user_bp.route("/users_list/")
def user_info():
# 返回的是user/chain/home.html文件
return render_template('home.html')
@user_bp.route('/books/')
def book_list():
return "图书列表"
<!-- demo4\user\chain\home.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- users.static中的users对应的是蓝本文件中name参数,而不是文件的名称 -->
<!-- filename='admin_css.css'中的admin_css.css文件的名字必须和blue_static的名字相同 -->
<link rel="stylesheet" href="{{ url_for('users.static', filename='admin_css.css') }}">
</head>
<body>
<p>蓝本-网站的主页</p>
<a href="{{ url_for("users.book_list") }}">url_for反转蓝图</a>
</body>
</html>
子域名的实现方式
- 如果你使用蓝图,那么在创建蓝图对象的时候,需要传递一个subdomain参数,来指定这个子域名的前缀crm_bp = Blueprint('crm', __name__, subdomain="crm");接着你需要在app.py这个主文件中配置app.config['SERVER_NAME'] = "chain.com:5000";最后要在Windows下找到C:\Windows\System32\drivers\etc文件夹中的hosts文件,添加域名与本机的映射,例如127.0.0.1 chain.com和127.0.0.1 crm.chain.com。
- 需要注意的是IP地址和localhost是不能有子域名的。
- Windows生成目录树的cmd命令:首先进入到项目的盘d:;其次是通过cd命令进入到项目的根文件;最后使用tree /f >list.txt生成目录树。
项目的结构
│ app.py
│ list.txt
│
├─blue_prints
│ │ crm.py
│ │ __init__.py
│ │
│ └─__pycache__
│ crm.cpython-36.pyc
│ __init__.cpython-36.pyc
│
├─static
├─templates
│ index.html
│
└─__pycache__
app.cpython-36.pyc
# chain\demo5\app.py
from flask import Flask
from blue_prints.crm import crm_bp
app = Flask(__name__)
# 必须要加上端口号
app.config['SERVER_NAME'] = "chain.com:5000"
app.register_blueprint(crm_bp)
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
# chain\demo5\blue_prints\crm.py
from flask import Blueprint, render_template
crm_bp = Blueprint('crm', __name__, subdomain="crm")
# 这个/是必须的,不能有其它的字符了
@crm_bp.route("/")
def crm():
return render_template('index.html')
本文暂时没有评论,来添加一个吧(●'◡'●)