【学习笔记】PythonWeb(Ⅰ)—— Flask

本文详细介绍了Python轻量级Web框架Flask,包括Flask概述、优点、缺点以及与Django的区别。接着深入讲解了Flask的基础知识,如路由创建、URL反转、响应、模板引擎Jinja2、蓝图、子域名、钩子、请求和响应对象,以及错误处理。此外,还涵盖了Flask进阶内容,如类视图、自定义装饰器、Jinja2模板的高级用法、消息传递机制、文件上传、Cookie和Session的使用。文章最后讨论了Flask的数据库交互,包括数据库连接、表模型的关联关系、初始化应用和解决循环引用问题。
摘要由CSDN通过智能技术生成

Flask

1、Flask介绍

安装依赖:pip install flask

      1.1、Flask概述

        Flask 是一个轻量级的 Python Web 框架,由 Armin Ronacher 开发。它被设计为简单而灵活的工具,用于快速构建 Web 应用程序和 API。Flask 的设计理念是尽可能地简单、易于理解和扩展,同时保持灵活性和可定制性。它采用了轻量级的核心和模块化的架构,允许开发者根据项目的需求选择性地添加功能

      1.2、Flask优点

        ① 简单易学: Flask 的设计简单直观,容易上手,对于初学者来说学习曲线较低
        ② 轻量级: Flask 的核心库非常小巧,没有过多的依赖,因此运行效率高
        ③ 灵活性: Flask 的架构非常灵活,开发者可以根据需要选择性地添加功能,而不受框架本身的限制
        ④ 可扩展性: Flask 支持使用扩展包来添加额外的功能,例如用户认证、数据库集成等
        ⑤ 文档丰富: Flask 拥有丰富的官方文档和社区支持,开发者可以很容易地找到所需的帮助和资源

      1.3、Flask缺点

        ① 过于灵活: 有时候灵活性可能会导致开发者需要自行选择和配置各种功能,增加了开发的复杂度
        ② 不适合大型应用: Flask 是一个微框架,对于大型复杂的应用程序来说,可能需要开发者自行处理一些问题,如结构组织、性能优化等,对于大型复杂的应用程序可以使用 Django 框架进行开发

      1.4、Flask和Django的区别

        ① 复杂度: Django 是一个全功能的高级 Web 框架,提供了大量的内置功能和组件,适用于构建大型、复杂的 Web 应用。而 Flask 更注重的是简单和灵活,更适合于快速开发小型项目或者原型
        ② 组件和功能: Django 自带了很多功能,如 ORM、认证系统、管理后台等,而 Flask 的核心功能相对较少,需要通过扩展来实现类似的功能
        ③ 学习曲线: Django 的学习曲线相对于 Flask 来说可能更陡峭一些,因为它提供了更多的功能和约定。相比之下,Flask 的学习曲线相对较低
        ④ 适用场景: Django 适用于构建大型、复杂的 Web 应用,尤其是企业级应用。而 Flask 更适合于快速原型开发、小型项目或者需要高度定制的应用

      1.5、Flask初体验

from flask import Flask

app = Flask(__name__) # 创建一个app

@app.route('/') # 装饰器
def hello_world(): # 视图函数
     return 'hello world'

if __name__ == "__main__":
    app.run() # 运行服务 默认是监听本机的5000端口

Tip:可以在run中传入debug=True,即 app.run(debug=True),以开启调试模式,这样在服务运行期间修改代码就可以不用重启服务

2、基础篇

      2.1、路由创建

@app.route()的参数 描述
第一个位置参数 请求路径
静态参数路由举例:/index
动态参数路由举例:/index/<int:id>
methods 当前视图函数支持的请求方式,请求方式不区分大小写,默认为GET
endpoint 指定路由的名称,在路由十分长的时候endpoint就显得十分好用,默认为路由对应的视图函数名
defaults 默认参数设置,必须在视图函数中定义一个形参来接收
redirect_to 用于设置永久重定向
strict_slashes 设置路由路径匹配是否为严格模式 —— 指定路由的斜杠严格性,默认不设置为严格路由匹配模式
当 strict_slashes 设置为 True 时,Flask 会严格区分 URL 的末尾是否有斜杠
from flask import Flask, render_template, request, session, redirect, url_for

# 创建一个 Flask 应用程序的实例
app = Flask(__name__)
# 静态路由
@app.route('/')  # http://127.0.0.1:5000/
def index():  # 视图函数
    return 'Hello World!'  # 返回字符串


# 动态路由
@app.route('/hello/<string:name>/<int:age>')  # http://127.0.0.1:5000/hello/niki/23
def hello(name, age):
    return f'Hello {
     name},your age is {
     age}'


# 动态路由(指定变量)
@app.route('/hi/name=<string:name>')  # http://127.0.0.1:5000/hi/name=niki
def hi(name):
    return f'Hi {
     name}'


# methods请求方法
@app.route('/login', methods=['GET', 'POST'])  # http://127.0.0.1:5000/login
def login():
    if request.method == 'POST':
        print('使用了POST方法')
    elif request.method == 'GET':
        print('使用了GET方法')


# 给路由起名endpoint
@app.route('/test1', endpoint='ep_test1')  # http://127.0.0.1:5000/test1
def test1():
    print(app.view_functions)  # endpoint和视图函数的映射:{'static': <function Flask.__init__.<locals>.<lambda> at 0x00000251C3FCFE18>, 'ep_test1': <function test1 at 0x00000251C5053400>}
    print(app.url_map)  # endpoint和理由的映射:Map([<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>, <Rule '/test1' (HEAD, OPTIONS, GET) -> ep_test1>])
    return 'test1'


# 默认参数defaults
@app.route('/test2', defaults={
   'name': 'niki'})  # http://127.0.0.1:5000/test2
def test2(name):
    return 'My name is {}'.format(name)


# 重定向redirect_to
@app.route('/test3', redirect_to='/')  # http://127.0.0.1:5000/test3
def test3():
    return '起始页'


# 严格模式strict_slashes
@app.route('/strict_true',strict_slashes=True)  # 报错:http://127.0.0.1:5000/strict_true/   通过:http://127.0.0.1:5000/strict_true/
def strict_true():
    return f'严格模式匹配{
     url_for("strict_true")}'
@app.route('/strict_false/',strict_slashes=False)  # 通过:http://127.0.0.1:5000/strict_false/   通过:http://127.0.0.1:5000/strict_false/
def strict_false():
    return f'非严格模式路由匹配{
     url_for("strict_false")}'


if __name__ == '__main__':
    app.run()  # 启用Flask应用程序

        注意点 ① :在指定变量名的动态路由中,以name=<string:name>为例,第一个name是 URL 中的变量名,第二个 name 是 Flask 路由中的一种类型转换器 string 的名字,它用于将 URL 中指定的 name 变量的值转换为字符串类型, URL 中的变量名跟类型转换器的名字可以不同,但是类型转换器的名字需要与视图函数的参数名相同,例如:

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

# 创建一个 Flask 应用程序的实例
app = Flask(__name__)

@app.route('/hi/name=<string:nn>')  # http://127.0.0.1:5000/hi/name=niki
def hi(nn):
    return f'Hi {
     nn}'


if __name__ == '__main__':
    app.run()  # 启用Flask应用程序

        注意点 ② :除了使用原有的类型转换器,可以使用自定义转换器

安装依赖:pip install werkzeug(Werkzeug 是 Flask 的底层 WSGI 工具库,它提供了一些信号,允许在应用程序处理请求的不同阶段插入自定义代码)

from flask import Flask, render_template, request, session, redirect, url_for
from werkzeug.routing import BaseConverter

# 创建一个 Flask 应用程序的实例
app = Flask(__name__)


# 用于匹配 URL 中的正则表达式
class RegexConverter(BaseConverter):
    def __init__(self, url_map, regex):
        # 重写父类定义方法
        super(RegexConverter, self).__init__(url_map)
        self.regex = regex

    def to_python(self, value):  # 重写了父类方法,用于将匹配到的字符串值转换为 Python 对象
        print('to_python方法被调用')
        return value


# 将自定义转换器注册到 Flask 应用程序的转换器字典中
app.url_map.converters['re'] = RegexConverter


@app.route("/index/<re('1\d{4}'):value>")  # 匹配一个以数字1开头、后面跟着4位数字的字符串,匹配到的字符串值会被传递给路由函数的参数value
def index(value):
    print(value)
    return "Hello World!"


if __name__ == '__main__':
    app.run()  # 启用Flask应用程序

        注意点 ③ :url_map 是一个 Werkzeug 的 Map 对象,它包含了 Flask 应用程序中所有路由规则的信息;view_functions 是一个字典,它将路由规则与视图函数进行了映射

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

app = Flask(__name__)


@app.route('/getinfo/<string:name>', endpoint='getinfo')
def getinfo(name):
    return f'Hello {
     name}'


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


if __name__ == '__main__':
    print('-' * 15, 'url_map', '-' * 15, '\n', app.url_map)
    print('-' * 15, 'view_functions', '-' * 15, '\n', app.view_functions)
    print('-' * 60)
    app.run()  # 启用Flask应用程序
#运行结果
--------------- url_map --------------- 
 Map([<Rule '/login' (OPTIONS, HEAD, GET) -> login>,
 <Rule '/getinfo/<name>' (OPTIONS, HEAD, GET) -> getinfo>,
 <Rule '/static/<filename>' (OPTIONS, HEAD, GET) -> static>])
--------------- view_functions --------------- 
 {
   'static': <function Flask.__init__.<locals>.<lambda> at 0x0000028ACA54FE18>, 'getinfo': <function getinfo at 0x0000028ACB5C21E0>, 'login': <function login at 0x0000028ACB5C2F28>}
------------------------------------------------------------

        注意点 ④ :可以add_url_rule(rule,endpoint=None,view_func=None)来注册路由

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

app = Flask(__name__)

def index():
    return "Hello World!"
app.add_url_rule('/', view_func=index)

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

      2.2、url_for 实现 URL 反转

        URL 反转是一种 Web 开发中常用的技术,用于根据路由规则和参数生成对应的 URL,在 Flask 中,url_for 函数就是用于实现 URL 反转的工具。通过调用 url_for 函数并传递相应的视图函数名称以及需要的参数,可以动态地生成与该视图函数对应的 URL
        URL 反转的好处在于,可以通过视图函数找到对应的URL ,这对于URL需要经常发生变化或者URL非常长的场景中十分实用

from flask import Flask, render_template, request, session, redirect, url_for, make_response, abort
from urllib.parse import unquote

app = Flask(__name__)


@app.route('/')  # 普通
def index():
    return 'Hello World'


@app.route('/profile/<username>')  # 携带参数
def getusername(username):
    return f'My name is {
     username}'


@app.route('/a/b/c/d/e/f/g?name=<username>', endpoint="profile")  # 使用endpoint
def profile(username):
    return f'{
     username}\'s profile'


if __name__ == '__main__':
    with app.test_request_context():
        print(url_for('index'))
        print(url_for('getusername', username='niki'))
        print(unquote(url_for("profile", username="niki")))  # 对URL中的特殊字符进行解码
#运行结果
/
/profile/niki
/a/b/c/d/e/f/g?name=niki

test_request_context() :是 Flask 提供的一个上下文管理器,用于模拟请求环境,它允许在测试环境中调用 Flask 视图函数,而无需实际发送 HTTP 请求,通常在编写 Flask 应用程序的单元测试时,若希望测试视图函数的行为,但不希望通过发送 HTTP 请求来进行测试,那么在这种情况下可以使用 test_request_context() 来创建一个虚拟的请求上下文,以便在其中调用视图函数

url_for解析出来的路径中的问号、等于号会被编码,需要通过urllib.parse.unquote进行解码

      2.3、响应

            2.3.1 响应字符串

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

app = Flask(__name__)


@app.route('/')
def index():
    return "Hello World!" # 响应字符串


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

            2.3.2 重定向

状态码 描述
301 Moved Permanently
(永久重定向)
当服务器返回状态码为 301 时,表示请求的资源已经永久移动到了新的位置
客户端收到 301 响应后会自动更新书签和缓存,并且以后的请求会直接发送到新的 URL
这种重定向通常用于网站进行结构调整或更改域名时,告知搜索引擎新的 URL 地址
302 Found
(临时重定向)
当服务器返回状态码为 302 时,表示请求的资源暂时移动到了一个新的位置
客户端收到 302 响应后会暂时重定向到新的 URL,但不会更新书签和缓存,后续请求仍然会发送到原始 URL
这种重定向通常用于临时性的页面重定向,比如暂时性的维护或临时性的资源移动
308 Permanent Redirect
(永久重定向)
当服务器返回状态码为 308 时,表示请求的资源已经永久移动到了新的位置
与 301 相比,308 明确指示客户端在重定向时不得更改请求方法,即若原始请求是 POST,则在重定向后应该继续使用 POST
这种状态码可以确保在进行重定向时不会丢失原始请求的请求体(例如 POST 请求的表单数据)
from flask import Flask, render_template, request, session, redirect, url_for, make_response, abort

app = Flask(__name__)


@app.route('/')
def index():
    return "Hello World!"


@app.route('/a', redirect_to='/')  # 在路由装饰器中指定redirect_to 状态码是308 表示永久重定向
def a():
    return "a"


@app.route('/b')
def b():
    return redirect('/')  # return一个redirect 默认状态码是302 表示临时重定向


@app.route('/c')
def c():
    return redirect('/', code=301)  # 指定状态码为301,表示永久重定向


@app.route('/d')
def d():
    return redirect(url_for('index'))  # 除了使用路径,还可以使用url_for(视图函数名) 同样的默认状态码是302


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

在浏览器中,如果访问一个会导致重定向的路由,如果状态码是301、308则再次访问时,状态码会变成200,这是因为对于永久重定向来说,客户端会对其进行缓存,302则反之,如果不想要缓存的话,可以在web开发者工具中勾选“禁用缓存”

在这里插入图片描述

            2.3.3 响应模板

        ① 概述:Flask 不包含模板引擎,而是选择了与其配合使用的 Jinja2 模板引擎,在视图函数中使用render_template()函数来给前端响应模板

① 模板引擎:用于生成动态内容的软件组件或系统,其通常接受模板和数据作为输入,并生成包含动态内容的输出文本
② Jinja2:是一个功能强大的 Python 模板引擎,由 Pocoo 开发,它提供了一种灵活而强大的方式来生成动态内容,包括 HTML、XML、JSON 等,Jinja2 支持模板继承、条件判断、循环迭代等高级特性,使得模板编写更加简洁和灵活,例如可以将HTML中的 { { 变量名 }} 解析成后端传入的变量值

        ② 文件结构:在 Flask 框架中,模板需要放置在templates中,这样在使用render_template ( 模板名 ) 的时候才能找到相应的 html 文件,例如:

根目录/
├── templates/
│   └── tpl.html
└── test.py

        ③ 模板内容:模板要符合Jinja模板的写法,例如变量要使用{ { 变量 }}、条件语句要使用{% if 条件 %}…{% endif %}、过滤器要在{ { }}中使用管道符“ | ” 等,例如编写tpl.html如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    {% if username %}
    <!-- 接收变量 -->
    <h1>Hello, {
  { username }}!</h1>
    {% endif %}

    {% if age %}
    <h1>My age is {
  { age }}, my gender is {
  { other.gender }}</h1>
    <!--  使用过滤器  -->
    <h1>My hobby is {
  { other.hobby|join(",") }}</h1>
    {% endif %}

</body>
</html>

        ④ 视图函数:视图函数中要使用 return render_template(模板名) 来返回一个模板,该模板首先会交给Jinja引擎进行渲染以动态地生成内容,然后再返回给客户端,例如在test.py中编写如下:

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

app = Flask(__name__)


@app.route('/')
def index():
    username = 'niki'  #
    info = {
   
            'age': 23,
            'other': {
   
                      'gender': 'male',
                      'hobby': ['唱', '跳', 'rap', '篮球']
                     }
        	}
    return render_template('tpl.html', username=username, **info)


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

        运行结果:
在这里插入图片描述

      2.4、蓝图

        ① 概述:在 Flask 中,蓝图(Blueprint)是一种组织和管理应用程序路由和视图函数的机制。它允许将相关功能的路由、视图函数和静态文件组织到单独的模块中,以便更好地组织和管理代码

Blueprint ( name,import_name,url_prefix )
第一个参数是蓝图对象的名字,第二个参数是模块名,第三个参数是设置路由的前缀

        ② 文件结构

根目录/
├── source/				# 软件包
│   ├── __init__.py		# 用于创建 Flask 应用程序,做全局配置、初始化等工作
│   ├── static/			# 存放静态文件,例如css、js、图片等
│   ├── templates/		# 存放模板文件
│   └── views/			# 存放视图函数的模块文件
│   	├── country.py	# 包含处理与国家相关的请求的视图函数
│   	└── animal.py	# 包含处理与动物相关的请求的视图函数
├── utils/				# 存放工具模块
└── app.py				# 项目的入口文件

        ③ 在animal.py、country.py中创建蓝图对象以及定义蓝图中的路由

#country.py
from flask import Blueprint

# 创建了个 Blueprint 对象,命名为 "book",并指定了模块的名称为 __name__,表示当前模块
# 使用 url_prefix 参数设置了 Blueprint 的 URL 前缀为 "/country"。这意味着后续定义的路由都会以 "/country" 开头
ct = Blueprint("country", __name__, url_prefix="/country")


# 定义了三个路由和视图函数
@ct.route("/")
def index():
    return "Hello Country!"


@ct.route("/China")
def China():
    return "Hello China!"


@ct.route("/France")
def France():
    return "Hello France!"
#animal.py
from flask import Blueprint

am = Blueprint("animal", __name__, url_prefix="/animal")

@am.route("/")
def index():
    return "Hello Animal!"


@am.route("/Dog")
def Dog():
    return "Hello Dog!"


@am.route("/Cat")
def Cat():
    return "Hello Cat!"

        ④ 在source/ __init__ .py编写创建flask实例的函数create_app(),并在 flask 实例中注册两个蓝图,这样两个蓝图的路由将被引用到 flask 实例 app 身上

from flask import Flask
from .views import animal, country

def create_app():
    # 创建了一个 Flask 应用实例
    app = Flask(__name__)
    # 使用 source.register_blueprint() 方法注册 views 模块中的 animal 和 country 蓝图对象
    # 这样,animal 蓝图中的路由和视图函数以及 country 蓝图中的路由和视图函数就成为了应用程序的一部分
    app.register_blueprint(animal.am)
    app.register_blueprint(country.ct)
    # 返回
    return app

        ⑤ 在app.py这个项目入口文件中运行 flask 实例,这样就可以像该服务发送请求了

from source import create_app

app = create_app()

if __name__ == '__main__':
    app.run() # 运行后访问http://127.0.0.1:5000/country/China

需要注意的是:source/ __init__ .py中要引用views中的模块,from .views(views前有一点) 而不是from views(views前没有一点),这是因为我们在app.py处运行程序,如果写from views的话,则views会从app.py所在的目录(即根目录)出发找,即这个views意为与根目录/app.py同级的文件夹,如果找不到就会报错;而from .views意为source.views,要达到这种效果,要保证写from .views的文件是一个软件包(有__init__.py的文件夹)的直接子文件且不是直接运行该文件,这样 . 就会被翻译成软件包,显然.views被翻译成source.views之后,app.py首先能找到与之同级的source,再能找到source下的views,也就不会报错了

拓展:如果直接运行source/__init__.py则会报错,因为直接运行该文件了,此时.views会被翻译成__main__.views,也就是会把当前的文件当作一个文件夹,然后去找文件夹下的views,显然运行的文件怎么会是文件夹呢,所以因为改成views(前面不要一点),此时就会被翻译成source/__init__.py所在文件夹下的views,也就不会报错了

      2.5、蓝图子域名

类型 举例
主域名设置 app.config[‘SERVER_NAME’]=‘hyh.com:5000’

        ① 以管理员方式打开cmd,输入notepad打开记事本,打开位于 C:\Windows\System32\drivers\etc 下的域名重定向文件 hosts,为里面添加一条记录,例如127.0.0.1 hyh.com(大家可以自定义设置),这样在访问域名hyh.com解析为本主机127.0.0.1了
        ② 在上面的source/__init__.py文件中设置app.config[‘SERVER_NAME’]来指定主域名,便可以通过http://hyh.com:5000/来进行访问

from flask import Flask
from .views import animal, country


def create_app():
    # 创建了一个 Flask 应用实例
    app = Flask(__name__)
    # 添加一个路由
    @app.route('/')
    def index():
        return 'Hello World!'
    # 使用 source.register_blueprint() 方法注册 views 模块中的 animal 和 country 蓝图对象
    # 这样,animal 蓝图中的路由和视图函数以及 country 蓝图中的路由和视图函数就成为了应用程序的一部分
    app.register_blueprint(animal.am)
    app.register_blueprint(country.ct)
    # 设置主域名
    app.config['SERVER_NAME'] = 'hyh.com:5000'
    # 返回
    return app

Tip:如果打开了vpn的话,可能需要先关掉一下,不然VPN 可能会拦截对 hyh.com 的 DNS 解析请求,并将其重定向到 VPN 提供的 DNS 服务器,而所写的 Flask 应用实际上只在本地主机上运行,VPN 不知道该域名应该解析到哪里,因此可能无法正确访问 hyh.com

类型 举例
子域名 bp=Blueprint(‘admin’,name,subdomain=‘admin’)

① 同样需要在host文件中插入一条127.0.0.1 niki.hyh.com的记录,niki便作为hyh.com下的子域名
② 以animal.py为例,在创建蓝图对象时传入subdomain参数值以设置子域名,便可以通过http://niki.hyh.com:5000/来进行访问

from flask import Blueprint

am = Blueprint("animal", __name__, url_prefix="/animal", subdomain="niki")


@am.route("/")
def index():
    return "Hello Animal!"


@am.route("/Dog")
def Dog():
    return "Hello Dog!"


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值