文章目录
我的网站: https://pythoneers.cn
1. Flask初识
1. Flask概述
Flask框架是一个短小精悍且可扩展强的Web框架,同Django框架一样依赖于第三方实现socket模块。
Flask特有上下文管理机制!
2. wsgi
WEB服务网关接口,是一个协议。Flask 中实现该协议的模块有 wsgiref
和 werkzeug
,模块本质上就是 socket服务端用于接收客户端的请求并处理
。
面试中可能会被用到wsgi是什么?
3. 安装Flask
一般使用 pip 安装 Flask:pip install Flask
,如果想加速下载,可以指定国内阿里云 pipy 的源,安装方法是:pip install flask -i https://mirrors.aliyun.com/pypi/simple
4. werkzurg
Flask 中实现 wsgi 的模块是 werkzurg,它是Flask第三方模块。
from werkzeug.serving import run_simple
def run(environ, start_response):
return 'Hello Flask!'
if __name__ == '__main__':
run_simple('localhost', 5000, run)
from werkzeug.wrappers import Request, Response
from werkzeug.serving import run_simple
@Request.application
def hello_flask(request):
return Response('Hello Flask!')
if __name__ == '__main__':
run_simple('localhost', 5000, hello_flask)
Django中实现wsgi的模块是wsgiref
5. 第一个Flask应用
from flask import Flask
app = Flask(__name__) # 实例化Flask类
@app.route('/index')
def index():
return 'Hello Flask!'
if __name__ == '__main__':
app.run() # run方法启动socket
2. 配置文件的使用
1. 配置文件导入原理
给一个类的字符串路径,找到类并且获取类中大写的静态字段。
main.py:
class FlaskClass:
FLAG = True
test.py:
import importlib
path = 'setting.FlaskClass'
p, c = path.rsplit('.', maxsplit=1)
# print(p, c) # setting FlaskClass
m = importlib.import_module(p)
# print(m) # <module 'setting' from 'C:\\Users\\Thanlon\\PycharmProjects\\flask_pro\\setting.py'>
cls = getattr(m, c)
print(cls) # 找到类:<class 'setting.FlaskClass'>
# print(dir(cls)) # 类成员
for key in dir(cls):
if key.isupper():
print('key:', key)
print('value:', getattr(cls, key))
"""
<class 'setting.FlaskClass'>
key: FLAG
value: True
"""
2. 配置文件的使用
Flask配置文件是一个 flask.config.Config
对象,它继承字典,默认配置文件如下:
print(type(app.config), app.config)
"""
<class 'flask.config.Config'>
"""
{
'ENV': 'production',
'DEBUG': False,
'TESTING': False,
'PROPAGATE_EXCEPTIONS': None,
'PRESERVE_CONTEXT_ON_EXCEPTION': None,
'SECRET_KEY': None,
'PERMANENT_SESSION_LIFETIME': datetime.timedelta(days=31),
'USE_X_SENDFILE': False,
'SERVER_NAME': None, 'APPLICATION_ROOT': '/',
'SESSION_COOKIE_NAME': 'session',
'SESSION_COOKIE_DOMAIN': None,
'SESSION_COOKIE_PATH': None,
'SESSION_COOKIE_HTTPONLY': True,
'SESSION_COOKIE_SECURE': False,
'SESSION_COOKIE_SAMESITE': None,
'SESSION_REFRESH_EACH_REQUEST': True,
'MAX_CONTENT_LENGTH': None,
'SEND_FILE_MAX_AGE_DEFAULT': datetime.timedelta(seconds=43200), 'TRAP_BAD_REQUEST_ERRORS': None,
'TRAP_HTTP_EXCEPTIONS': False,
'EXPLAIN_TEMPLATE_LOADING': False,
'PREFERRED_URL_SCHEME': 'http',
'JSON_AS_ASCII': True,
'JSON_SORT_KEYS': True,
'JSONIFY_PRETTYPRINT_REGULAR': False,
'JSONIFY_MIMETYPE': 'application/json',
'TEMPLATES_AUTO_RELOAD': None,
'MAX_COOKIE_SIZE': 4093}
修改默认配置文件,创建配置文件 settings.py
,用于存放修改的配置文件:
settings.py
:
# 存放开发环境、线上环境、测试环境共有的配置文件
class Config(object):
pass
# 线上环境配置文件
class ProductionConfig(Config):
DEBUG = False
# 开发环境配置文件
class DevelopmentConfig(Config):
DEBUG = True
# 测试环境配置文件
class TestingConfig(Config):
DEBUG = True
app.py:
from flask import Flask
app = Flask(__name__)
# print(type(app.config), app.config)
app.config.from_object('settings.DevelopmentConfig') # 引入配置文件
if __name__ == '__main__':
app.run()
3. 路由系统
1. 反向生成URL
使用 url_for
方法生成 url
:
@app.route('/index', methods=['GET', 'POST'], endpoint='name')
def index():
print(url_for('name'))
return 'Flask!'
使用 url_for
方法生成url时设置参数:
@app.route('/index/<int:id>')
def index(id):
print(url_for('index', id=1))
return 'Flask!'
"""
/index/1
"""
如果不指定 endpoint
的值,默认为函数名:
def _endpoint_from_view_func(view_func):
assert view_func is not None, 'expected view func if endpoint ' \
'is not provided.'
return view_func.__name__ # 返回函数名
def add_url_rule(self,rule,endpoint=None,view_func=None,provide_automatic_options=None, **options):
if endpoint is None:
endpoint = _endpoint_from_view_func(view_func)
options['endpoint'] = endpoint
methods = options.pop('methods', None)
@app.route('/index', methods=['GET', 'POST'])
def index():
print(url_for('index'))
return 'Flask!'
如果endpoint非要重名,必须保证函数相同。
2. 动态路由的构造
@app.route('/index/<username>') # 没有指定类型是字符串类型
@app.route('/index/<int:id>')
@app.route('/index/<float:id>')
@app.route('/index/<path:path>')
示例:
@app.route('/index/<float:id>')
def index(id):
print(type(id),id)
return 'Flask!'
浏览器输入:http://127.0.0.1:5000/index/1.0
控制台输出:<class 'float'> 1.0
4. 请求与响应相关数据
1. 请求相关数据
常用请求的相关信息:
request.method
request.args
request.form
request.cookies
request.headers
其它请求的相关信息:
request.values
request.path
request.full_path
request.script_root
request.url
request.base_url
request.url_root
request.host_url
request.host
request.files
obj = request.files['the_file_name']
obj.save('/uploads/'+secure_filename(f.filename))
2. 响应响应体
响应字符串:
@app.route('/index')
def index():
return 'Flask' # 字符串
响应模板:
@app.route('/index')
def index():
return render_template() # 模板
响应重定向:
@app.route('/index')
def index():
return redirect('') # 重定向
响应json格式数据 [from flask import json
]:
@app.route('/index')
def index():
return json.dumps({
'name': 'thanlon'}) # {'name': 'thanlon'}
另一种响应json格式数据的方式 [from flask import jsonify
]:
@app.route('/index')
def index():
return jsonify({
'name': 'thanlon'}) # {'name': 'thanlon'}
不能响应字典,即return dic是错误的!
3. 响应响应头
以上是响应响应体,还可以响应响应头和cookie,只需要从Flask模块中导入make_response进行封装即可。(from flask import make_response
)
示例:将字符串类型的响应体与响应头和cookie一同封装响应:
@app.route('/index')
def index():
obj = make_response('Flask')
obj.headers['response_flag'] = 'response_info'
obj.set_cookie('key','value')
return obj
响应模板、响应重定向、响应json格式数据和示例就一样的。
5. 简单登录案例
1. 案例目录结构
2. 案例主要代码
app.py:
from flask import Flask, render_template, request, redirect, session
# app = Flask(__name__, static_folder='static', static_url_path='/thanlon') # 可指定前缀
app = Flask(__name__)
app.secret_key = 'thanlon'
@app.route('/login', methods=['GET', 'POST']) # 默认允许GET请求
def login():
if request.method == 'GET':
return render_template('login.html')
username = request.form.get('username')
pwd = request.form.get('pwd')
if username == 'Thanlon' and pwd == '123':
session['username'] = username # 默认session保存在签名或加密的cookie中
return redirect('/index')
# return render_template('login.html', error='用户名或者密码错误!')
return render_template('login.html', **{
'error': '用户名或者密码错误!'})
@app.route('/index') # 默认允许GET请求
def index():
username = session.get('username')
if not username:
return redirect('/login')
return render_template('index.html')
if __name__ == '__main__':
app.run()
login.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>登录</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<div class="container">
<div class="row" style="margin-top:30px">
<div class="col-md-4 col-md-offset-4">
<div class="panel panel-primary">
<div class="panel-heading">登录</div>
<div class="panel-body">
<form method="POST">
<div class="form-group">
<label>用户名</label>
<input type="text" name="username" class="form-control" id="" placeholder="Username"
required="required">
</div>
<div class="form-group">
<label>密码</label>
<input type="password" name="pwd" class="form-control" placeholder="Password"
required="required">
<div style="color:red">{
{error}}</div>
</div>
<button type="submit" class="btn btn-primary">登录</button>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js"
integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
crossorigin="anonymous"></script>
</html>
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>首页</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<div class="container">
<div class="row" style="margin-top:30px">
<div class="panel panel-default">
<div class="panel-body">
欢迎{
{session['username']}}进入首页!
</div>
</div>
</div>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js"
integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
crossorigin="anonymous"></script>
</html>
3. 案例页面效果
登录失败:
登录成功:
6. 用户认证
1. 第一种用户认证
settings.py
:
# 存放开发环境、线上环境、测试环境共有的配置文件
class Config(object):
DEBUG = False
SECRET_KEY = 'thanlon'
app.py
:
from flask import Flask, render_template, redirect, url_for, request, session
app = Flask(__name__)
app.config.from_object('settings.DevelopmentConfig') # 引入配置文件
USER_DICT = {
1: {
'username': 'thanlon', 'age': '23', 'gender': '男'},
2: {
'username': 'kiku', 'age': '25', 'gender': '女'}
}
@app.route('/login', methods=['GET', 'POST']) # 默认允许GET请求
def login():
if request.method == 'GET':
return render_template('login.html')
username = request.form.get('username')
pwd = request.form.get('pwd')
if username == 'Thanlon' and pwd == '123':
session['username'] = username # 默认session保存在签名或加密的cookie中
return redirect('/index')
# return render_template('login.html', error='用户名或者密码错误!')
return render_template('login.html', **{
'error': '用户名或者密码错误!'})
@app.route('/index')
def hello_world():
if not session.get('username'):
return redirect(url_for('login'))
return render_template('index.html', user_dict=USER_DICT)
@app.route('/delete/<int:nid>')
def delete(nid):
if not session.get('username'):
return redirect(url_for('login'))
del USER_DICT[nid]
return redirect(url_for('hello_world')) # url_for('')写函数名,和路由无关
@app.route('/detail/<int:nid>')
def detail(nid):
if not session.get('username'):
return redirect(url_for('login'))
info = USER_DICT[nid]
return render_template('detail.html', info=info)
if __name__ == '__main__':
app.run()
index.html
:
……
<div class="container">
<div class="row">
<div class="col-md-4">
<table class="table table-hover">
<tr>
<th style="text-align: center">ID</th>
<th style="text-align: center">用户名</th>
<th style="text-align: center">年龄</th>
<th style="text-align: center">性别</th>
<th style="text-align: center">操作</th>
</tr>
{% for k,v in user_dict.items() %}
<tr style="text-align: center">
<td>{
{ k }}</td>
<td>{
{ v.username }}</td>
<td>{
{ v.age }}</td>
<td>{
{ v.gender }}</td>
<td><a href="/detail/{
{ k }}">详情</a> | <a href="/delete/{
{ k }}">删除</a></td>
</tr>
{# <tr>#}
{# <td>{
{ v['username'] }}</td>#}
{# <td>{
{ v['age'] }}</td>#}
{# <td>{
{ v['gender'] }}</td>#}
{# </tr> #}
{# <tr>#}
{# <td>{
{ v.get('usename','默认') }}</td>#}
{# <td>{
{ v.get('age') }}</td>#}
{# <td>{
{ v.get('gender') }}</td>#}
{# </tr>#}
{% endfor %}
</table>
</div>
</div>
</div>
……
detail.html
:
……
<body>
{#{'username': 'thanlon', 'age': '23', 'gender': '男'}#}
<br>
{% for item in info.values() %}{#% for item in info.values() %#}
{
{ item }}
{% endfor %}
</body>
2. 装饰器知识点补充
函数没有加装饰器:
# coding:utf-8
def auth(func):
def inner(*args, **kwargs):
ret = func(*args, **kwargs)
return ret
return inner
def index():
print('index')
print(index.__name__) # 打印函数名
'''index'''
函数加装饰器,让inner函数伪装成index函数:
# coding:utf-8
def auth(func):
def inner(*args, **kwargs):
ret = func(*args, **kwargs)
return ret
return inner
@auth
def index():
print('index')
@auth
def login():
print('login')
print(index.__name__