flask 之创建和运行
一、相关概念
1、python web框架
# python 中的web 框架 - django:大而全,内置很多 - flask:小而精,几乎没有内置,都需要用第三方解决 - fastapi:异步框架,号称效率高 - Sanic - Tornado - web.py... # flask 介绍 # 0 Flask是一个用Python编写的Web应用框架。它由Armin Ronacher开发,他领导着一个名为Pocco的国际Python爱好者团队。Flask基于Werkzeug WSGI工具包和Jinja2模板引擎。两者都是Pocco项目 # 1 Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用于接收http请求并对请求进行预处理,然后触发Flask框架,开发人员基于Flask框架提供的功能对请求进行相应的处理,并返回给用户,如果要返回给用户复杂的内容时,需要借助jinja2模板来实现对模板的处理,即:将模板和数据进行渲染,将渲染后的字符串返回给用户浏览器 # 2 “微”(micro) 并不表示你需要把整个 Web 应用塞进单个 Python 文件(虽然确实可以),也不意味着 Flask 在功能上有所欠缺。微框架中的“微”意味着 Flask 旨在保持核心简单而易于扩展。Flask 不会替你做出太多决策——比如使用何种数据库。而那些 Flask 所选择的——比如使用何种模板引擎——则很容易替换。除此之外的一切都由可由你掌握 # 3 默认情况下,Flask 不包含数据库抽象层、表单验证,或是其它任何已有多种库可以胜任的功能。然而,Flask 支持用扩展来给应用添加这些功能,如同是 Flask 本身实现的一样。众多的扩展提供了数据库集成、表单验证、上传处理、各种各样的开放认证技术等功能。Flask 也许是“微小”的,但它已准备好在需求繁杂的生产环境中投入使用 # 4 Flask depends on the Werkzeug WSGI toolkit, the Jinja template engine, and the Click CLI toolkit
2、
Werkzeug是一个WSGI工具包,他可以作为一个Web框架的底层库。这里稍微说一下, werkzeug 不是一个web服务器,也不是一个web框架,而是一个工具包,官方的介绍说是一个 WSGI 工具包,它可以作为一个 Web 框架的底层库,因为它封装好了很多 Web 框架的东西,例如 Request,Response 等等
Werkzeug 是一个综合性 WSGI Web 应用程序库。它最初是 WSGI 应用程序的各种实用程序的简单集合,现已成为最先进的 WSGI 实用程序库之一。 Werkzeug doesn’t enforce any dependencies. It is up to the developer to choose a template engine, database adapter, and even how to handle requests Werkzeug 不强制执行任何依赖关系。由开发人员选择模板引擎、数据库适配器,甚至如何处理请求 # https://werkzeug.palletsprojects.com/en/3.0.x/ from werkzeug.wrappers import Request, Response @Request.application def hello(request): return Response('Hello World!') if __name__ == '__main__': from werkzeug.serving import run_simple run_simple('localhost', 4000, hello)
3、
Jinja 是一个快速、富有表现力、可扩展的模板引擎。模板中的特殊占位符允许编写类似于 Python 语法的代码。然后向模板传递数据以渲染最终文档
4、
# flask 2 版本加入的---》定制命令的工具 python manage.py init_db # 介绍 # 1 Click is a Python package for creating beautiful command line interfaces in a composable way with as little code as necessary. It’s the “Command Line Interface Creation Kit”. It’s highly configurable but comes with sensible defaults out of the box Click 是一个 Python 包,用于以可组合的方式使用尽可能少的代码创建漂亮的命令行界面。它是“命令行界面创建工具包”。它具有高度可配置性,但具有开箱即用的合理默认值 # 2 It aims to make the process of writing command line tools quick and fun while also preventing any frustration caused by the inability to implement an intended CLI API 它的目的是使编写命令行工具的过程变得快速而有趣,同时也防止因无法实现预期的 CLI API 而造成的任何挫败感 # 3 Click in three points: arbitrary nesting of commands automatic help page generation supports lazy loading of subcommands at runtime
Click三点: 命令的任意嵌套 自动生成帮助页面 支持运行时延迟加载子命令
install
# pip3 install click import click @click.command() @click.option('--count', default=1, help='打印次数') @click.option('--name', prompt='你的名字',help='The person to greet.') def hello(count, name): for x in range(count): print(f"Hello {name}!") if __name__ == '__main__': hello() # 1 python3 app.py --count=3 # 2 python3 app.py --help # 3 python3 app.py --count=3 --name=lqz
二、创建和安装
1、安装 Flask 时会自动安装这些依赖
# 最新flask 要python 3.8 以上 # pip install flask # 3.x ##### 必选依赖 Werkzeug :wsgi的工具包 Jinja :模板渲染 MarkupSafe :解决了xss攻击 ItsDangerous:加密解密的---》session会用到 Click:定制命令的 Blinker :信号会用到 signal #### 可选依赖:这些依赖不会自动安装。如果您安装它们,Flask 将检测并使用它们
2、
是一个 Python 库,用于监控文件系统事件(如文件的创建、修改、删除等)。在 Flask 项目中,watchdog
可以用于自动重新加载程序,使开发过程更为高效,因为开发者在修改代码后不需要手动重启服务。
watchdog
通过监控文件系统中的更改来触发相应的事件。它适用于各种场景,例如:
- 自动化测试——在代码更改时自动运行测试。
- 文件同步——实时监控并同步文件变化。
- 自动构建——在代码或配置文件更改时自动重新构建项目。
watchdog
的核心组件是观察者(Observer)和事件处理程序(Event Handler)。
这里定义一个事件处理器类,观察者监听到文件的变化,事件处理器的处理逻辑在其中定义
from flask import Flask, render_template, g, session from watchdog.events import FileSystemEventHandler from watchdog.observers import Observer import threading import time import os app = Flask(__name__) app.secret_key = 'jingzhiz' class MyEventHandler(FileSystemEventHandler): # 当创建文件或文件夹时触发 def on_created(self, event): if event.is_directory: print(f'目录已创建: {event.src_path}') # 输出创建文件夹的名字 else: print(f'文件已创建: {event.src_path}') # 输出创建文件的名字 # 当删除文件或文件夹时触发 def on_deleted(self, event): if event.is_directory: print(f'目录已删除: {event.src_path}') # 输出删除文件夹的名字 else: print(f'文件已删除: {event.src_path}') # 这里可以处理文件删除事件 def start_observer(): event_handler = MyEventHandler() observer = Observer() # 设置监控的目录,并使其递归监控子目录 observer.schedule(event_handler, path='/Users/sanpangdan/Desktop/flasktest', recursive=True) observer.start() try: while True: time.sleep(1) # 让观察者保持运行状态 except KeyboardInterrupt: observer.stop() # 响应停止信号 observer.join() # 使用线程来运行 watchdog 的观察者 observer_thread = threading.Thread(target=start_observer) observer_thread.daemon = True # 设置为守护线程,以便主程序退出时该线程也能自动结束 observer_thread.start() @app.before_request def before_request(): # 在请求之前设置一些用户和会话信息 g.user = 'jingzhiz' session['info'] = {'爱好': '洗脚', '年龄': 18, '性别': '男'} @app.template_global() def add(a, b): print(a, b) return a + b # 在模板中提供一个全局函数用于加法运算 @app.template_filter() def reverse(s): return s[::-1] # 在模板中过滤器用于字符串反转 @app.route('/') def index(): return '这里是根路径返回的结果' # 根路径返回的简单字符串 @app.route('/home') def home(): return render_template('home.html') # 渲染 home.html 模板 if __name__ == '__main__': app.run() # 启动 Flask 应用
效果:
3、
# pip3 install python-dotenv import os from dotenv import load_dotenv from dotenv import dotenv_values
## 1 加载配置文件 # 必须在根路径下新建一个 .env 的文件,并写入配置才能返回True,会把.env下的配置文件设置进环境变量 # res=load_dotenv() # take environment variables from .env # # print(res) # print(os.environ.get('MYSQL_HOST')) # You will probably want to add .env to your .gitignore, especially if it contains secrets like a password. ## 2 获取环境变量字典 config = dotenv_values(".env") print(config) print(config.get('DOMAIN'))
4、虚拟环境
# linux 、mac # 创建虚拟环境 mkdir myproject cd myproject python3 -m venv .venv # 激活虚拟环境 . .venv/bin/activate # windos mkdir myproject cd myproject py -3 -m venv .venv # 激活虚拟环境 .venv\Scripts\activate
5、创建flask项目
# pip install flask # 写个py文件,写入 from flask import Flask app = Flask(__name__) @app.route('/') def index(): return 'hello world' # 运行项目方式 -方式一(pycharm配置): -新建一个flask-server---》配置选中 script---》有app的文件 -方式二:命令(推荐这种) flask py文件名字 app run flask --app 5-app.py run -方式三:命令 python38 -m flask --app py文件名字 run python38 -m flask --app 5-app.py run -方式四,右键运行 if __name__ == '__main__': app.run() -方式五:命令运行(跟右键运行一样) python38 5-app.py - 方式6:少用(pip install python-dotenv) flask app run
6、
pycharm中直接勾选
flask --app 5-app.py run --debug # 浏览器显示错误信息 # 自动重启
三、fastapi
1、简单的返回字符串页面
from fastapi import FastAPI import asyncio app = FastAPI()
@app.get("/") async def read_root(): # 如果有io await asyncio.sleep(2) return {"Hello": "World"} # uvicorn 6-fastapi快速体验:app
2、官网
四、显示用户小案例
1、找模版
Examples · Bootstrap v5 中文文档 v5.3 | Bootstrap 中文网 (bootcss.com)
2、flask 代码
from flask import Flask, request, render_template, redirect, session, jsonify app = Flask(__name__) # 如果要用session,必须加这一句 app.secret_key = 'asdfasdfasdf-lqz-justin' USERS = { 1: {'name': '刘亦菲', 'age': 18, 'gender': '男', 'text': "刘亦菲,1987年8月25日出生于湖北省武汉市,华语影视女演员、歌手,毕业于北京电影学院2002级表演系本科", 'img': 'https://img2.woyaogexing.com/2021/10/16/e3ccba623848430ba83209c0621a2256!400x400.jpeg'}, 2: {'name': '彭于晏', 'age': 28, 'gender': '男', 'text': "彭于晏,1982年3月24日出生于中国台湾省澎湖县,毕业于不列颠哥伦比亚大学,华语影视男演员。。。。。。。。", 'img': 'https://img2.woyaogexing.com/2021/10/16/e71aa35728c34313bccb4c371192990f!400x400.jpeg'}, 3: {'name': '迪丽热巴', 'age': 38, 'gender': '女', 'text': "迪丽热巴(Dilraba),1992年6月3日出生于中国新疆乌鲁木齐市,毕业于上海戏剧学院,中国内地影视女演员", 'img': 'https://img2.woyaogexing.com/2021/10/30/6a34146dde2d4f1c832463a5be1ed027!400x400.jpeg'}, 4: {'name': '亚瑟', 'age': 38, 'gender': '男', 'text': "亚瑟,是腾讯手游《王者荣耀》中一名战士型英雄角色,也是《王者荣耀》的新手英雄之一,既可攻又可守", 'img': 'https://img2.woyaogexing.com/2021/10/30/371b2aa7a03c4f53b7b1bc86f877d7d1!400x400.jpeg'}, } @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'GET': return render_template('login.html') else: username = request.form.get('username') password = request.form.get('password') if username == 'lqz' and password == '123': # 登录成功,把登录信息,写入到session中 session['username'] = username # 登录成功,重定向到首页 # return redirect('http://www.baidu.com') return redirect('/') else: return render_template('login.html', error='用户名或密码错误') @app.route('/', methods=['GET']) def index(): username = session.get('username') if username: return render_template('index.html', user_dict=USERS) else: return redirect('/login') @app.route('/detail/<int:pk>') def detail(pk): username = session.get('username') if username: user = USERS.get(pk) return render_template('detail.html', user=user) else: return redirect('/login') ''' flask中学到的 1 request是全局: request.method request.path 2 request.form 前端post提交的数据 3 request.args 前端get请求提交的数据 4 路由注册是使用装饰器 @app.route('/detail/<int:pk>') def detail(pk): 5 路由有转换器:int 。。。。 /detail/<int:pk> /detail/?pk=1 6 新手四件套 return '字符串' return render_template('index.html', user_dict=USERS) return redirect('/login') return jsonify(字典,列表) 7 session的使用--全局导入--必须加秘钥 放值:session['key']=value 取值:session.get('key') '''
补充:request.args.get
和 request.form.get 的区别和用法
2.1、 request.args.get
- 用途: 用于获取 URL 查询字符串(query string)中的参数。这些参数通常通过 GET 请求方式传递。
- 用法:
request.args.get('param_name', default=None, type=None)
'param_name'
: 要获取的参数名称。default
(可选): 如果参数不存在,则返回默认值。type
(可选): 指定要将参数转换为的类型,例如int
。如果转换失败,将返回默认值
from flask import Flask, request app = Flask(__name__) @app.route('/search') def search(): query = request.args.get('q', default='', type=str) page = request.args.get('page', default=1, type=int) return f'Searching for: {query}, page: {page}' # 访问 /search?q=flask&page=2 将返回 'Searching for: flask, page: 2'
2.2、request.form.get
- 用途: 用于获取 POST 请求中表单提交的参数。通常用于处理来自 HTML 表单的数据。
- 用法:
request.form.get('param_name', default=None, type=None)
'param_name'
: 要获取的参数名称。default
(可选): 如果参数不存在,则返回默认值。type
(可选): 指定要将参数转换为的类型。
from flask import Flask, request app = Flask(__name__) @app.route('/submit', methods=['POST']) def submit(): username = request.form.get('username') password = request.form.get('password') return f'Username: {username}, Password: {password}' # 提交一个表单到 /submit 将提取 'username' 和 'password' 表单字段的值
区别
-
数据来源:
request.args.get
用于从 URL 查询参数中提取数据。request.form.get
用于从 POST 请求的表单数据中提取数据。
-
请求类型:
request.args.get
主要用于 GET 请求。request.form.get
主要用于 POST 请求。
在开发需要处理用户输入的 Flask 应用时,这两个方法可以帮助你轻松提取所需的数据,并处理不同的请求类型。有良好的默认值处理和类型转换选项,这可以增加代码的健壮性
补充:
request.get_json
- 用途:用于获取以 JSON 格式通过
POST
方法发送的数据。 - 例子:对于
Content-Type: application/json
的请求,用data = request.get_json()
获取 JSON 数据,然后通过data.get('key')
访问。
request.data
- 用途:用于获取原始请求体数据,适用于自定义格式的数据。
- 注意:通常用于处理非标准数据格式。
request.values
- 用途:这是
args
和form
的结合,可以同时获取查询参数和表单数据。 - 例子:
request.values.get('key')
。
request.files
- 用途:用于获取上传的文件(例如通过一个 HTML 表单上传的文件)。
- 例子:
file = request.files['file_field']
可以用于访问上传的文件。
request.headers
- 用途:用于获取请求头信息。
- 例子:
request.headers.get('User-Agent')
可以用于获取用户代理信息。
3、html 页面代码
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css"> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script> <title>Title</title> </head> <body> <div class="container"> <!-- 头部--> <div class="sticky-top"> <header class="d-flex flex-wrap justify-content-center py-3 mb-4 border-bottom"> <a href="/" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-dark text-decoration-none"> <svg class="bi me-2" width="40" height="32"> <use xlink:href="#bootstrap"></use> </svg> <span class="fs-4">交友平台</span> </a> <ul class="nav nav-pills"> <li class="nav-item"><a href="#" class="nav-link active" aria-current="page">首页</a></li> <li class="nav-item"><a href="#" class="nav-link">女生</a></li> <li class="nav-item"><a href="#" class="nav-link">男生</a></li> <li class="nav-item"><a href="#" class="nav-link">国产</a></li> <li class="nav-item"><a href="#" class="nav-link">欧美</a></li> </ul> </header> </div> <!--轮播图--> <div> <div class="bd-example-snippet bd-code-snippet"> <div class="bd-example"> <div id="carouselExampleCaptions" class="carousel slide" data-bs-ride="carousel"> <div class="carousel-indicators"> <button type="button" data-bs-target="#carouselExampleCaptions" data-bs-slide-to="0" class="" aria-label="Slide 1"></button> <button type="button" data-bs-target="#carouselExampleCaptions" data-bs-slide-to="1" aria-label="Slide 2" class="active" aria-current="true"></button> <button type="button" data-bs-target="#carouselExampleCaptions" data-bs-slide-to="2" aria-label="Slide 3" class=""></button> </div> <div class="carousel-inner"> <div class="carousel-item"> <img src="https://img.zcool.cn/community/01fb5458fedf57a801214550f9677a.jpg@2o.jpg" alt="" width="100%" height="300"> <div class="carousel-caption d-none d-md-block"> <h5>激情绿荫</h5> <p>Some representative placeholder content for the first slide.</p> </div> </div> <div class="carousel-item active"> <img src="https://img2.baidu.com/it/u=2951612437,4135887500&fm=253&fmt=auto&app=138&f=JPEG" alt="" width="100%" height="300"> <div class="carousel-caption d-none d-md-block"> <h5>品牌雨伞</h5> <p>Some representative placeholder content for the second slide.</p> </div> </div> <div class="carousel-item"> <img src="https://img1.baidu.com/it/u=1417689082,3333220267&fm=253&fmt=auto&app=138&f=JPEG" alt="" width="100%" height="300"> <div class="carousel-caption d-none d-md-block"> <h5>家装节</h5> <p>Some representative placeholder content for the third slide.</p> </div> </div> </div> <button class="carousel-control-prev" type="button" data-bs-target="#carouselExampleCaptions" data-bs-slide="prev"> <span class="carousel-control-prev-icon" aria-hidden="true"></span> <span class="visually-hidden">Previous</span> </button> <button class="carousel-control-next" type="button" data-bs-target="#carouselExampleCaptions" data-bs-slide="next"> <span class="carousel-control-next-icon" aria-hidden="true"></span> <span class="visually-hidden">Next</span> </button> </div> </div> </div> </div> <!-- 内容--> <div class="row row-cols-md-2" style="padding: 10px"> {% for k,v in user_dict.items() %} <div class="card"> <div class="row " style="padding: 10px"> <img src="{{v.get('img')}}" alt="" class="col-md-4"> <div class="col-md-8"> <div class="card-body"> <h5 class="card-title">{{v['name']}}</h5> <p class="card-text">{{v.text}}</p> <p class="card-text"> <a href="/detail/{{k}}" class="btn btn-danger">查看详细</a> </p> </div> </div> </div> </div> {%endfor%} </div> <!-- table--> <div class="bd-example" style="margin-top: 30px"> <table class="table table-hover table-striped table-bordered"> <thead> <tr class="table-danger"> <th colspan="3" class="text-center">更多交友</th> </tr> </thead> <tbody> <tr class="table-success"> <th>杨幂</th> <td>女</td> <td>33</td> </tr> <tr class="table-warning"> <th scope="row">刘亦菲</th> <td>未知</td> <td>40</td> </tr> <tr class="table-success"> <th scope="row">彭于晏</th> <td>男</td> <td>23</td> </tr> <tr class="table-warning"> <th scope="row">陈奕迅</th> <td>男</td> <td>44</td> </tr> <tr class="table-success"> <th scope="row">薛之谦</th> <td>男</td> <td>36</td> </tr> <tr class="table-warning"> <th>李清照</th> <td>女</td> <td>未知</td> </tr> </tbody> </table> </div> <!--分页--> <div class="d-flex justify-content-center"> <ul class="pagination pagination-lg"> <li class="page-item"> <a class="page-link" href="#" aria-label="Previous"> <span aria-hidden="true">«</span> </a> </li> <li class="page-item"><a class="page-link" href="#">1</a></li> <li class="page-item"><a class="page-link" href="#">2</a></li> <li class="page-item"><a class="page-link" href="#">3</a></li> <li class="page-item"><a class="page-link" href="#">4</a></li> <li class="page-item"><a class="page-link" href="#">5</a></li> <li class="page-item"><a class="page-link" href="#">6</a></li> <li class="page-item"> <a class="page-link" href="#" aria-label="Next"> <span aria-hidden="true">»</span> </a> </li> </ul> </div> <!-- 尾部--> <div> <footer class="py-3 my-4"> <ul class="nav justify-content-center border-bottom pb-3 mb-3"> <li class="nav-item"><a href="#" class="nav-link px-2 text-muted">首页</a></li> <li class="nav-item"><a href="#" class="nav-link px-2 text-muted">特性</a></li> <li class="nav-item"><a href="#" class="nav-link px-2 text-muted">联系我们</a></li> <li class="nav-item"><a href="#" class="nav-link px-2 text-muted">资料获取</a></li> <li class="nav-item"><a href="#" class="nav-link px-2 text-muted">关于</a></li> </ul> <p class="text-center text-muted">Copyright © 1998 - 2029 liuqingzheng. All Rights Reserved. </p> </footer> </div> </div> </body> </html>
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css"> <title>Title</title> </head> <body> <div class="container col-xl-10 col-xxl-8 px-4 py-5"> <div class="row align-items-center g-lg-5 py-5"> <div class="col-lg-7 text-center text-lg-start"> <h1 class="display-4 fw-bold lh-1 mb-3">亚洲最大交友平台</h1> <p class="col-lg-10 fs-4">Bootstrap是Twitter推出的一个用于前端开发的开源工具包。它由Twitter的设计师Mark Otto和Jacob Thornton合作开发,是一个CSS/HTML框架。目前,Bootstrap最新版本为5.0</p> </div> <div class="col-md-10 mx-auto col-lg-5"> <form class="p-4 p-md-5 border rounded-3 bg-light" method="post"> <div class="form-floating mb-3"> <input type="text" class="form-control" id="floatingInput" placeholder="name@example.com" name="username"> <label for="floatingInput">用户名</label> </div> <div class="form-floating mb-3"> <input type="password" class="form-control" id="floatingPassword" placeholder="Password" name="password"> <label for="floatingPassword">密码</label> </div> <div class="checkbox mb-3"> <label> <input type="checkbox" value="remember-me"> 记住密码 </label> </div> <button class="w-100 btn btn-lg btn-primary" type="submit">登录</button> <hr class="my-4"> <small class="text-muted">{{error}}</small> </form> </div> </div> </div> </body> </html>
detail.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css"> <title>Title</title> </head> <body> <div class="container col-xl-10 col-xxl-8 px-4 py-5"> <div class="row align-items-center g-lg-5 py-5"> <div class="col-lg-7 text-center text-lg-start"> <h1 class="display-4 fw-bold lh-1 mb-3">亚洲最大交友平台</h1> <p class="col-lg-10 fs-4">Bootstrap是Twitter推出的一个用于前端开发的开源工具包。它由Twitter的设计师Mark Otto和Jacob Thornton合作开发,是一个CSS/HTML框架。目前,Bootstrap最新版本为5.0</p> </div> <div class="col-md-10 mx-auto col-lg-5"> <form class="p-4 p-md-5 border rounded-3 bg-light" method="post"> <div class="form-floating mb-3"> <input type="text" class="form-control" id="floatingInput" placeholder="name@example.com" name="username"> <label for="floatingInput">用户名</label> </div> <div class="form-floating mb-3"> <input type="password" class="form-control" id="floatingPassword" placeholder="Password" name="password"> <label for="floatingPassword">密码</label> </div> <div class="checkbox mb-3"> <label> <input type="checkbox" value="remember-me"> 记住密码 </label> </div> <button class="w-100 btn btn-lg btn-primary" type="submit">登录</button> <hr class="my-4"> <small class="text-muted">{{error}}</small> </form> </div> </div> </div> </body> </html>