最近在学习python,以后会分享一些学习历程。
Flask 是一个用 Python 编写的轻量级 Web 应用框架。
Flask 基于 WSGI(Web Server Gateway Interface)和 Jinja2 模板引擎,旨在帮助开发者快速、简便地创建 Web 应用。
Flask 被称为"微框架",因为它使用简单的核心,用扩展增加其他功能。
详细学习可以参考:Flask 教程 | 菜鸟教程
pip install flask
首先创建一个类,主要做异常处理,打印等
from flask import Flask
class FlaskBase:
app = Flask(__name__)
def run(self):
self.app.run()
新建demo1.py,具体执行
from flask_base import FlaskBase
class Test1(FlaskBase):
@staticmethod
@FlaskBase.app.route("/", methods=['GET']) #使用父类路由
def index():
return "Hello Word!"
if __name__ == '__main__':
test1 = Test1()
test1.run()
打开浏览器访问http://127.0.0.1:5000 ,结果打印"Hello Word!"
2. 路由系统和变量
路由是将URL映射到视图函数的机制。Flask使用装饰器来定义路由
from flask import request, jsonify
@staticmethod
@FlaskBase.app.route("/sayHi1/username>", methods=['GET', 'POST'])
def sayHi1(username):
# 请求url则是/sayHi2/xxx
return username
@staticmethod
@FlaskBase.app.route("/sayHi3", methods=['GET', 'POST'])
def sayHi3():
# 通过request获取参数,请求url则是/sayHi3?username=xxx
if request.method == 'GET':
username = request.args.get('username', type=str)
# 此处欲发送post请求,需要在对应html文件的form表单中设置method为post
elif request.method == 'POST':
username = request.form.get('username')
data = {
'username': username
}
# 输出json
return jsonify(data)
Flask支持在URL中包含变量,类型可以是:
Flask 路由支持不同的 HTTP 请求方法,如 GET、POST、PUT、DELETE 等。可以通过 methods 参数指定允许的请求方法
@app.before_request
:在每个请求处理之前运行的函数。@app.after_request
:在每个请求处理之后运行的函数。@app.teardown_request
:在请求结束后运行的函数,用于清理工作。在FlaskBase类中加入
@app.before_request
def before_request():
print('Before request')
@app.after_request
def after_request(response):
print('After request')
return response
@app.teardown_request
def teardown_request(exception):
print('Teardown request')
url_for:
以帮助我们生成动态的 URL,而无需手动编写不断变化的 URL 地址。它的基本语法如下:
url_for(endpoint, **values)
其中,endpoint
参数是一个视图函数的名称,用于指示要生成 URL 的视图函数。values
参数是一些可选的关键字参数,用于传递给视图函数的参数。通过传递这些参数,我们可以在生成的 URL 中包含相应的参数值。
@staticmethod
@FlaskBase.app.route("/sayHi1/", methods=['GET', 'POST'])
def sayHi1(username):
# 重定向
return redirect(url_for('index'))
如果是index重定向sayHi1呢,这里需要使用endpoint。
endpoint:
说明:每个app中都存在一个url_map,这个url_map中包含了url到endpoint的映射;
作用:当request请求传来一个url的时候,会在url_map中先通过rule找到endpoint,然后再在view_functions中根据endpoint再找到对应的视图函数view_func
@staticmethod
@FlaskBase.app.route("/", methods=['GET'])
def index():
return redirect(url_for('sayHi1', username='你好!'))
@staticmethod
@FlaskBase.app.route("/sayHi1/", methods=['GET', 'POST'], endpoint='sayHi1')
def sayHi1(username):
return username
或者
def __init__(self):
# 显式注册路由并指定端点名称
FlaskBase.app.add_url_rule('/', 'index', self.index, methods=['GET'])
FlaskBase.app.add_url_rule('/sayHi1/', 'sayHi1', self.sayHi1, methods=['GET', 'POST'])
@staticmethod
def index():
return redirect(url_for('sayHi1', username='你好!'))
@staticmethod
def sayHi1(username):
return username
@staticmethod
@FlaskBase.app.route("/login", methods=['GET', 'POST'])
def login():
global username, password
if request.method == 'GET':
username = request.args.get('username', type=str, default="")
password = request.args.get('password', type=str, default="")
# 此处欲发送post请求,需要在对应html文件的form表单中设置method为post
elif request.method == 'POST':
username = request.form.get('username', default="")
password = request.form.get('password', default="")
if username and password:
return 'login successful!'
else:
abort(404)
return None
# 自定义错误处理方法,将404这个error与Python函数绑定
# 当需要抛出404error时,将会访问下面的代码
@staticmethod
@FlaskBase.app.errorhandler(404)
def handle_404_error(err):
# return "发生了错误,错误情况是:%s"%err
# 自定义一个界面
return render_template('404.html')
404.html
页面丢失了
目录结构:
your_project/
├── templates/
│ └── 404.html
├── static/
│ └── error404.png
├── flask_base.py
└── demo1.py (包含Test1类的文件)
欲实现url与视图函数的绑定,除了使用路由装饰器@app.route,还可以通过add_url_rule(rule,endpoint=None,view_func=None)方法,其中:
rule:设置的url
endpoint:给url设置的名称
view_func:指定视图函数的名称
示例在介绍重定向的时候已经给了,可以参考
flask_base.py
from flask import Flask, views
# 定义父视图类继承基类View
class ViewBase(views.View):
def __init__(self):
super(ViewBase, self).__init__()
# 实例属性
self.context = {
'ads': '测试view!'
}
from flask import render_template
from flask_base import FlaskBase, ViewBase
# 标准类视图
# 创建应用实例
app_instance = FlaskBase()
# 定义子视图类继承父类并实现工程
class Index(ViewBase):
def dispatch_request(self):
return render_template('view_module/index.html', **self.context)
class Login(ViewBase):
def dispatch_request(self):
return render_template('view_module/login.html', **self.context)
class Register(ViewBase):
def dispatch_request(self):
return render_template('view_module/register.html', **self.context)
app_instance.app.add_url_rule(rule='/', endpoint='index', view_func=Index.as_view('index'))
app_instance.app.add_url_rule(rule='/login/', endpoint='login', view_func=Login.as_view('login'))
app_instance.app.add_url_rule(rule='/register/', endpoint='register', view_func=Register.as_view('register'))
if __name__ == '__main__':
app_instance.run()
from flask import render_template, request, views
from flask_base import FlaskBase
@FlaskBase.app.route('/')
def index():
return render_template('index.html')
# 定义LoginView类
class LoginView(views.MethodView):
# 定义get函数
def get(self):
return render_template("index.html")
# 定义post函数
def post(self):
username = request.form.get("username")
password = request.form.get("password")
if username == 'admin' and password == 'admin':
return "用户名正确,可以登录!"
else:
return "用户名或密码错误,不可以登录!"
app_instance = FlaskBase()
# 注册类视图
# 未设置endpoint,则endpoint默认为as_view设置的类视图名
app_instance.app.add_url_rule('/login', view_func=LoginView.as_view('loginview'))
if __name__ == '__main__':
print("有效路由:", app_instance.app.url_map)
app_instance.run()
index.html
Title
访问http://127.0.0.1:5000/login
from flask_base import FlaskBase
@FlaskBase.app.route('/')
def hello_world():
print('Hello World!')
return 'Hello World!'
# 定义装饰器函数
def user_login(func): # func是被装饰的函数
def inner(): # 定义包裹函数(实际执行的新函数)
print('登录操作!') # 新增的额外功能
func() # 执行原始函数的功能
# 此处如果return inner(),那么返回的是inner函数的执行结果
# 而使用return inner,则返回的是inner函数
return inner
# 定义新闻页面视图函数news
@user_login # 等价于 news = user_login(news)
def news():
print(news.__name__)
print('这是新闻详情页!')
news() # 实际调用的是inner()
app_instance = FlaskBase()
if __name__ == '__main__':
app_instance.run()
执行结果:
登录操作!
inner
这是新闻详情页
这里存在一个问题,就是print(news.__name__)执行结果是inner,另外如果我们的方法需要传参如何实现。
第一个问题,可以使用functools.wraps
方法来保留原函数的属性与名称。直接上代码
from flask_base import FlaskBase
from functools import wraps
@FlaskBase.app.route('/')
def hello_world():
print('Hello World!')
return 'Hello World!'
# 定义装饰器函数
def user_login(func): # func是被装饰的函数
'''
def func(*args, **kwargs):
* :代表元组,长度不限;
** :代表键值对,个数不限;
*args:指用元组传参,元组内包含不定个数的位置参数;
** kwargs:指用字典传参,字典内包含不定个数的关键字参数(键值对);
'''
@wraps(func)
def inner(*args, **kwargs): # inner函数接收参数
print('登录操作!') # 新增的额外功能
# 执行传入函数时使用inner接收到的参数
func(*args, **kwargs) # 执行原始函数的功能
# 此处如果return inner(),那么返回的是inner函数的执行结果
# 而使用return inner,则返回的是inner函数
return inner
# 定义新闻页面视图函数news
@user_login # 等价于 news = user_login(news)
def news():
print(news.__name__)
print('这是新闻详情页!')
news() # 实际调用的是inner()
@user_login
def news_list(*args):
page = args[0]
print(news_list.__name__)
print('这是新闻列表页的第' + str(page) + '页!')
news_list(1) # 实际调用的是inner()
app_instance = FlaskBase()
if __name__ == '__main__':
app_instance.run()
执行结果:
登录操作!
news
这是新闻详情页!
登录操作!
news_list
这是新闻列表页的第1页!
上述类视图、装饰器分别通过继承、包装的方式减少了单个flask程序文件里重复代码的出现,实现了程序的优化。但是这样处理后的文件内,不同功能的代码块(类视图、视图函数)仍然混杂在一起。如果要制作一个非常大型的程序项目,这样不仅会让代码阅读变得十分困难,而且不利于后期维护。为了解决这一问题,我们需要引入蓝图(flask.Blueprint),用于实现程序功能的模块化
主路由视图函数:创建flask对象,并为拓展模块中的蓝图对象提供注册入口
from flask_base import FlaskBase
from view_blueprint import News, Product
@FlaskBase.app.route('/')
def hello_world():
print('Hello World!')
return 'Hello World!'
app_instance = FlaskBase()
app_instance.app.register_blueprint(News.new_list)
# url_prefix ,等同于product_list = Blueprint('products', __name__, url_prefix='/index')
app_instance.app.register_blueprint(Product.product_list, url_prefix='/index')
if __name__ == '__main__':
app_instance.run()
分路由视图函数:创建蓝图对象,实现功能拓展
from flask import Blueprint
class News:
# 实例化蓝图对象,参数一类似于蓝图对象的名称
# 一个app下的蓝图对象不可重名
# url_prefix:路由前缀
new_list = Blueprint('news', __name__, url_prefix='/index')
# 蓝图对象的使用和app类似
# 一个蓝图下的视图函数名、endpoint不可重复
@staticmethod
@new_list.route('/news')
def new():
return '这是新闻模块!'
class Product:
# 实例化蓝图对象,参数一类似于蓝图对象的名称
# 一个app下的蓝图对象不可重名
product_list = Blueprint('products', __name__)
# 蓝图对象的使用和app类似
# 一个蓝图下的视图函数名、endpoint不可重复
@staticmethod
@product_list.route('/products')
def new():
return '这是产品模块!'
分别请求:
http://127.0.0.1:5000/index/news
http://127.0.0.1:5000/index/products