优秀的编程知识分享平台

网站首页 > 技术文章 正文

「AIPyGo」Flask入门教程(flask快速入门)

nanyue 2024-10-15 11:34:39 技术文章 4 ℃


参考文档:

  • flask官方文档:https://flask.palletsprojects.com/en/1.1.x
  • jinja2官方文档:https://jinja.palletsprojects.com/en/2.11.x/templates/

基本使用

简介

安装

搭建环境

mkdir flask_demo
cd flask_demo
python -m venv venv
source venv/bin/activate

安装flask

pip install Flask

入门案例

最小的flask应用

# 导入flask
from flask import Flask

# 创建app
app = Flask(__name__)

# 创建路由
@app.route('/')
def hello_world():
    return 'Hello, lxgzhw!'

if __name__ == '__main__':
    app.run(debug=True)

运行方式

python main.py

添加路由

示例

# 添加一个新的路由
@app.route('/hello')
def hello():
    return {"name":"lxgzhw","age":22}

完整代码

# 导入flask
from flask import Flask

# 创建app
app = Flask(__name__)

# 创建路由
@app.route('/')
def hello_world():
    return 'Hello, lxgzhw!'

# 添加一个新的路由
@app.route('/hello')
def hello():
    return {"name":"lxgzhw","age":22}

if __name__ == '__main__':
    app.run(debug=True)

常用配置

配置静态文件目录

说明:默认静态文件目录是根目录下的static文件夹,或模块下的static文件夹

注意

  • 该文件夹需要我们手动创建
  • 该文件夹是相对于main.py的位置而言的,main.py所在目录的static文件夹会被作为静态文件夹使用

请求

总结

flask中如何处理请求

  • 把请求封装在了request对象中,从request中,我们可以获取post提交的表单数据get提交的请求参数文件上传的文件cookie

响应

添加自定义响应头

核心代码

# 构造一个响应对象
resp = make_response(render_template('404.html'), 404)
# 给响应对象的headers属性,添加一个键值对(像字典)
resp.headers['X-Something'] = 'lxgzhw'
# 返回响应对象
return resp

全部代码

from flask import Flask, make_response
from flask import abort, redirect, url_for
from flask import render_template

app = Flask(__name__)


@app.route('/')
def index():
    # 重定向,跳转到登录页面
    return redirect(url_for('login'))


@app.route('/login')
def login():
    # 直接返回错误信息
    # 401 Unauthorized 权限校验未通过
    # abort(401)
    # 200 301 404 500
    # abort(404)
    abort(404)


# 全局错误处理器
@app.errorhandler(404)
def page_not_found(error):
    resp = make_response(render_template('404.html'), 404)
    resp.headers['X-Something'] = 'lxgzhw'
    return resp


if __name__ == '__main__':
    app.run(debug=True)

返回json

如果我们返回的是一个字典,flask会自动帮我们把字典转换为json字符串,然后响应给前端

from flask import Flask

app = Flask(__name__)


@app.route("/")
def me_api():
    user = {
        'username': 'lxgzhw',
        'age': 22,
        'gender': '男'
    }
    # 返回一个字典
    return user


if __name__ == '__main__':
    app.run(debug=True)

如果我们响应的是列表嵌套字典的结构,我们可以使用jsonify函数,将其转化为json数据字符串

from flask import Flask, jsonify

app = Flask(__name__)


@app.route("/")
def users_api():
    # 返回的是一个列表嵌套字典,也能够使用jsonify转换为json格式
    users = [
        {'name': f"root{i}", 'age': 22, 'gender': '男'}
        for i in range(20)
    ]
    return jsonify(users)


if __name__ == '__main__':
    app.run(debug=True)

路由

路径参数

示例1:安全的路径参数

# markupsafe 安全标记
from markupsafe import escape

# 定义了一个路由
@app.route('/user/<username>')
def show_user_profile(username):# 将username原封不动的搬下来,表示这是一个路径参数
    # 返回路径参数
    # 防止黑客注入攻击
    return '你输入的用户名是 %s' % escape(username)

示例2:将路径参数转换为int类型

# <int:post_id>,说明post_id是一个int类型
@app.route('/post/<int:post_id>')
def show_post(post_id):
    # 打印路径参数
    return '你输入的数字是: %d' % post_id

示例3:将路径参数转换为路径类型

# path:subpath,转换成路径类型
@app.route('/path/<path:subpath>')
def show_subpath(subpath):
    # 打印
    return '你输入的路径是: %s' % escape(subpath)

参考:flask支持的所有路径参数转换类型

路径构造器

url_for的基本用法

  • 能够根据路由函数的名称,反向找到该路由函数对应的“路由路径”
print(url_for('index'))#index是路由函数名称:解析得到url路径
print(url_for('login'))
  • 能够传递查询参数
# url_for可以添加其他的参数
# 如果说,没有路径参数:则,添加的参数会被作为get查询参数传递
print(url_for('login', hello='world'))
  • 能够指定路径参数
# 可以替换路径参数
print(url_for('profile', username='lxgzhw'))

完整案例

from flask import Flask, url_for
from markupsafe import escape

app = Flask(__name__)

# 首页路由
@app.route('/')
def index():
    return 'index'

# 登录路由
@app.route('/login')
def login():
    return 'login'

# 用户/用户名
@app.route('/login/<username>')
def profile(username):
    return '{}\'s profile'.format(escape(username))

# 测试
with app.test_request_context():#在每次flask重新加载的时候,都会执行
    print("-----------------------------")
    print(url_for('index'))#index是路由函数名称:解析得到url路径
    print(url_for('login'))
    # url_for可以添加其他的参数
    # 如果说,没有路径参数:则,添加的参数会被作为get查询参数传递
    print(url_for('login', hello='world'))
    # 可以替换路径参数
    print(url_for('profile', username='lxgzhw'))
    
if __name__ == '__main__':
    app.run(debug=True)

HTTP方法

常见的HTTP方法有

  • GET:查询
  • PUT:查询
  • POST:传数据,一般用来新增数据
  • PATCH:修改
  • DELETE:删除

规定路由支持哪些方法

from flask import Flask,request

app = Flask(__name__)

# 登录的路由:支持GET请求和POST请求
@app.route('/login', methods=['GET', 'POST'])
def login():
    # 如果是POST请求
    if request.method == 'POST':
        return "这是一个POST请求"
    # 如果是GET请求
    else:
        return "这是一个GET请求"
    
if __name__ == '__main__':
    app.run(debug=True)

重定向与错误

from flask import Flask
from flask import abort, redirect, url_for

app = Flask(__name__)


@app.route('/')
def index():
    # 重定向,跳转到登录页面
    return redirect(url_for('login'))


@app.route('/login')
def login():
    # 直接返回错误信息
    # 401 Unauthorized 权限校验未通过
    # abort(401)
    # 200 301 404 500
    # abort(404)
    abort(500)


if __name__ == '__main__':
    app.run(debug=True)

模板

基本使用

第一步:在视图函数中渲染模板

from flask import Flask, request
from flask import render_template

app = Flask(__name__)


@app.route('/')
@app.route('/index')
def hello():
    return render_template('index.html', name="理想国真惠玩")


if __name__ == '__main__':
    app.run(debug=True)

第二步:编写templates/index.html

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>Bootstrap 101 Template</title>

    <!-- Bootstrap -->
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
      rel="stylesheet"
    />

    <!-- HTML5 shim 和 Respond.js 是为了让 IE8 支持 HTML5 元素和媒体查询(media queries)功能 -->
    <!-- 警告:通过 file:// 协议(就是直接将 html 页面拖拽到浏览器中)访问页面时 Respond.js 不起作用 -->
    <!--[if lt IE 9]>
      <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
      <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <h1>你好,世界!</h1>
    <p>欢迎您:{{name}}</p>
    <!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) -->
    <script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script>
    <!-- 加载 Bootstrap 的所有 JavaScript 插件。你也可以根据需要只加载单个插件。 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
  </body>
</html>

在flask模板中渲染变量的方式

{{ name }}

登录案例

目录结构

main.py
templates
    index.html
    login.html

main.py

from flask import Flask, request
from flask import render_template

app = Flask(__name__)


@app.route('/')
@app.route('/index')
def hello():
    return render_template('index.html', name="理想国真惠玩")


@app.route('/login', methods=['POST', 'GET'])
def login():  # 登录
    error = ""  # 错误信息
    if request.method == 'POST':  # post请求
        # 校验用户名和密码
        username = request.form['username']
        password = request.form['password']
        print(f"用户传过来的用户名是:{username},密码是:{password}")
        if username == 'lxgzhw' and password == 'lxgzhw':
            return render_template("index.html", name=username)
        else:
            # 记录错误信息
            error = '用户名或密码错误'
    # GET请求 POST请求失败
    return render_template('login.html', error=error)


if __name__ == '__main__':
    app.run(debug=True)

index.html

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>Bootstrap 101 Template</title>

    <!-- Bootstrap -->
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
      rel="stylesheet"
    />

    <!-- HTML5 shim 和 Respond.js 是为了让 IE8 支持 HTML5 元素和媒体查询(media queries)功能 -->
    <!-- 警告:通过 file:// 协议(就是直接将 html 页面拖拽到浏览器中)访问页面时 Respond.js 不起作用 -->
    <!--[if lt IE 9]>
      <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
      <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <h1>你好,世界!</h1>
    <p>欢迎您:{{name}}</p>
    <div>
      <ul>
        <li>
          <a href="/login">登录</a>
        </li>
      </ul>
    </div>
    <!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) -->
    <script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script>
    <!-- 加载 Bootstrap 的所有 JavaScript 插件。你也可以根据需要只加载单个插件。 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
  </body>
</html>

login.html

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>Bootstrap 101 Template</title>

    <!-- Bootstrap -->
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
      rel="stylesheet"
    />

    <!-- HTML5 shim 和 Respond.js 是为了让 IE8 支持 HTML5 元素和媒体查询(media queries)功能 -->
    <!-- 警告:通过 file:// 协议(就是直接将 html 页面拖拽到浏览器中)访问页面时 Respond.js 不起作用 -->
    <!--[if lt IE 9]>
      <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
      <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <div class="container-fluid">
      <div class="row">
        <div class="col-md-6 col-md-offset-3">
          <h1>用户登录</h1>
          <br />
          <div class="alert alert-danger" role="alert">{{error}}</div>
          <br />
          <form method="post" action="/login">
            <div class="form-group">
              <label for="username">用户名</label>
              <input
                type="text"
                class="form-control"
                id="username"
                name="username"
              />
            </div>
            <div class="form-group">
              <label for="password">密码</label>
              <input
                type="password"
                class="form-control"
                id="password"
                name="password"
              />
            </div>
            <button type="submit" class="btn btn-default">立即登录</button>
          </form>
        </div>
      </div>
    </div>
    <!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) -->
    <script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script>
    <!-- 加载 Bootstrap 的所有 JavaScript 插件。你也可以根据需要只加载单个插件。 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
  </body>
</html>

运行方式

python main.py

访问:http://127.0.0.1:5000

提示:如何获取get传递的参数

searchword = request.args.get('key', '')

文件上传

main.py

from flask import Flask, request
from flask import render_template

app = Flask(__name__)


@app.route('/', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        # 获取文件对象
        f = request.files['file_name']
        # 保存文件
        f.save('./111.jpg')
    return render_template('upload.html')


if __name__ == '__main__':
    app.run(debug=True)

upload.html

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>Bootstrap 101 Template</title>

    <!-- Bootstrap -->
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
      rel="stylesheet"
    />

    <!-- HTML5 shim 和 Respond.js 是为了让 IE8 支持 HTML5 元素和媒体查询(media queries)功能 -->
    <!-- 警告:通过 file:// 协议(就是直接将 html 页面拖拽到浏览器中)访问页面时 Respond.js 不起作用 -->
    <!--[if lt IE 9]>
      <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
      <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <div class="container-fluid">
      <div class="row">
        <div class="col-md-6 col-md-offset-3">
          <h1>文件上传</h1>
          <br />
          <br />
          <form method="post" action="/" enctype="multipart/form-data">
            <div class="form-group">
              <label for="upload">文件上传</label>
              <input type="file" id="upload" name="file_name" />
              <p class="help-block">请选择要上传的文件</p>
            </div>
            <button type="submit" class="btn btn-default">立即上传</button>
          </form>
        </div>
      </div>
    </div>
    <!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) -->
    <script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script>
    <!-- 加载 Bootstrap 的所有 JavaScript 插件。你也可以根据需要只加载单个插件。 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
  </body>
</html>

注意

  • 前端上传文件,form表单必须添加enctype="multipart/form-data"
  • 一定要定义上传文件的名称<input type="file" id="upload" name="file_name" />
  • 后端通过文件名称获取文件对象f = request.files['file_name']
  • 调用文件的保存方法可以直接保存文件f.save('./111.jpg')

常用语法

if语句

{% if name %}
  <h1>Hello {{ name }}!</h1>
{% else %}
  <h1>Hello, World!</h1>
{% endif %}

for语句

{% with messages = get_flashed_messages() %}
    {#如果存在#}
    {% if messages %}
        {#打印所有的闪现消息#}
        <ul class=flashes>
            {% for message in messages %}
                <li>{{ message }}</li>
            {% endfor %}
        </ul>
    {% endif %}
{% endwith %}

动态生成url

<a href="{{ url_for('login') }}">登录</a>

模板继承

模板继承主要用于解决代码重复问题,比如头部,底部,左侧,菜单等,可能大多数页面都会使用,就可以添加在模板中,被其他的html继承。

这里有个简单的案例

templates/layout.html

<!doctype html>
<title>AIPyGo</title>
<h1>这里是头部内容</h1>
{#获取所有闪现消息#}
{% with messages = get_flashed_messages() %}
    {#如果存在#}
    {% if messages %}
        {#打印所有的闪现消息#}
        <ul class=flashes>
            {% for message in messages %}
                <li>{{ message }}</li>
            {% endfor %}
        </ul>
    {% endif %}
{% endwith %}

{#用来定义一个块,这个块能够被继承,实现,重写#}
{% block body %}{% endblock %}
<div>这里是底部的内容</div>

templates/index.html

{#继承layout.html#}
{% extends "layout.html" %}
{#重写body代码块#}
{% block body %}
    <h3>这里是重写的内容</h3>
    <ul>
        <li>
            <a href="{{ url_for('login') }}">登录</a>
        </li>
    </ul>
{% endblock %}

main.py

from flask import Flask, render_template

app = Flask(__name__)


@app.route('/')
def index():
    return render_template('index.html')


@app.route('/login')
def login():
    return "登录页面"


if __name__ == '__main__':
    app.run(debug=True)

运行方法

python main.py

全局变量

cookie

设置cookie

from flask import make_response

@app.route('/set_cookie')
def set_cookie():
    # 创建一个响应对象
    resp = make_response(render_template('index.html'))
    # 响应对象上,有一个set_cookie的方法:当字典中,set(键,值)
    resp.set_cookie('username', '理想国真惠玩')
    # 返回响应
    return resp

读取cookie

from flask import request

@app.route('/')
def read_cookie():
    # session,只在服务器端可以访问
    # 缺点:很不安全 auth, cookie 只读:禁止JavaScript读取
    # 从cookie当中读取
    # cookie可以当做是字典来用, get [] 遍历
    username = request.cookies.get('username')
    return render_template('index.html', name=username)

session

基本使用

# 导入
from flask import session

# 增加
session[key] = value

# 获取
value = session.get(key)

# 删除
session.pop(key, None)

完整案例

from flask import Flask, session, redirect, url_for, request, render_template
from markupsafe import escape

app = Flask(__name__)

# 生成了一个秘钥
# 生成方法:python -c 'import os; print(os.urandom(16))'
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'


@app.route('/')
def index():
    # 如果session中有username这个key
    # 取session
    username = session.get('username')
    if username:
        return render_template('index.html', name=username)
    return redirect(url_for('login'))


@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        # 存储session
        session['username'] = request.form['username']
        # 跳转到首页: index指的是 index函数
        return redirect(url_for('index'))
        # return redirect('/')
    return render_template('login.html')


@app.route('/logout')
def logout():
    # 删除session
    session.pop('username', None)
    # 跳转到首页
    return redirect(url_for('login'))


if __name__ == '__main__':
    app.run(debug=True)

flash

传递:参数1是闪现内容,参数2是闪现分类

flash(u'Invalid password provided', 'error')

获取

{% with messages = get_flashed_messages(with_categories=true) %}
  {% if messages %}
    <ul class=flashes>
    {% for category, message in messages %}
      <li class="{{ category }}">{{ message }}</li>
    {% endfor %}
    </ul>
  {% endif %}
{% endwith %}

过滤

{% with errors = get_flashed_messages(category_filter=["error"]) %}
{% if errors %}
<div class="alert-message block-message error">
  <a class="close" href="#">×</a>
  <ul>
    {%- for msg in errors %}
    <li>{{ msg }}</li>
    {% endfor -%}
  </ul>
</div>
{% endif %}
{% endwith %}

错误处理

全局错误处理器

from flask import Flask
from flask import abort, redirect, url_for
from flask import render_template

app = Flask(__name__)


@app.route('/')
def index():
    # 重定向,跳转到登录页面
    return redirect(url_for('login'))


@app.route('/login')
def login():
    # 直接返回错误信息
    # 401 Unauthorized 权限校验未通过
    # abort(401)
    # 200 301 404 500
    # abort(404)
    abort(404)


# 全局错误处理器
@app.errorhandler(404)
def page_not_found(error):
    return render_template('404.html'), 404


if __name__ == '__main__':
    app.run(debug=True)

日志

基本使用

from flask import Flask

app = Flask(__name__)

app.logger.info('消息级别的日志')
app.logger.debug('调试级别的日志')
# 默认只打印warning以上级别的日志到控制台
app.logger.warning('注意级别的日志 (%d 个错误)', 42)
app.logger.error('错误级别日志')

if __name__ == '__main__':
    app.run(debug=True)
最近发表
标签列表