【fastapi框架:jinja2模板、ORM操作、中间件与CORS】

news2024/11/14 13:54:24

## 五、jinja2模板

要了解jinja2,那么需要先理解模板的概念。模板在Python的web开发中⼴泛使⽤,它能够有效的将业务逻辑和页⾯逻辑分开,使代码可读性增强、并且更加容易理解和维护。
模板简单来说就是⼀个其中包涵占位变量表⽰动态的部分的⽂件,模板⽂件在经过动态赋值后,返回给⽤户。

jinja2是Flask作者开发的⼀个模板系统,起初是仿django模板的⼀个模板引擎,为Flask提供模板⽀持,由于其灵活,快速和安全等优点被⼴泛使⽤。

在jinja2中,存在三种语法:

> 1. 变量取值 {{ }}
> 2. 控制结构 {% %}

### 5.1、jinja2 的变量

`Main.py`

```python
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
import uvicorn

app = FastAPI()  # 实例化 FastAPI对象
templates = Jinja2Templates(directory="templates")  # 实例化Jinja2对象,并将文件夹路径设置为以templates命令的文件夹


@app.get('/')
def hello(request: Request):
    return templates.TemplateResponse(
        'index.html',
        {
            'request': request,  # 注意,返回模板响应时,必须有request键值对,且值为Request请求对象
            'user': 'yuan',
            "books": ["金瓶梅", "聊斋", "剪灯新话", "国色天香"],
            "booksDict": {
                "金瓶梅": {"price": 100, "publish": "苹果出版社"},
                "聊斋": {"price": 200, "publish": "橘子出版社"},
            }
        }
    )
if __name__ == '__main__':
    uvicorn.run("main:app", port=8080, debug=True, reload=True)

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


<h1>{{ user}}</h1>

<p>{{ books.0 }}</p>
<p>{{ books.1 }}</p>
<p>{{ books.2 }}</p>
<p>{{ books.3 }}</p>

<p>{{ booksDict.金瓶梅.price }}</p>


</body>
</html>

5.2、jinja2 的过滤器

变量可以通过“过滤器”进⾏修改,过滤器可以理解为是jinja2⾥⾯的内置函数和字符串处理函数。常⽤的过滤器有:

过滤器名称说明
capitialize把值的⾸字母转换成⼤写,其他⼦母转换为⼩写
lower把值转换成⼩写形式
title把值中每个单词的⾸字母都转换成⼤写
trim把值的⾸尾空格去掉
striptags渲染之前把值中所有的HTML标签都删掉
join拼接多个值为字符串
round默认对数字进⾏四舍五⼊,也可以⽤参数进⾏控制
safe渲染时值不转义

那么如何使⽤这些过滤器呢?只需要在变量后⾯使⽤管道(|)分割,多个过滤器可以链式调⽤,前⼀个过滤器的输出会作为后⼀个过滤
器的输⼊。

{{ 'abc'| captialize  }}  # Abc

{{ 'abc'| upper  }} # ABC

{{ 'hello world'| title  }} # Hello World

{{ "hello world"| replace('world','yuan') | upper }} # HELLO YUAN

{{ 18.18 | round | int }} # 18

5.3、jinja2 的控制结构

5.3.1、分支控制

jinja2中的if语句类似与Python的if语句,它也具有单分⽀,多分⽀等多种结构,不同的是,条件语句不需要使⽤冒号结尾,⽽结束控制语句,需要使⽤endif关键字

{% if age > 18 %}

    <p>成年区</p>

{% else %}
    
    <p>未成年区</p>

{% endif %}
5.3.2、循环控制

jinja2中的for循环⽤于迭代Python的数据类型,包括列表,元组和字典。在jinja2中不存在while循环。

{% for book in books %}
    <p>{{ book }}</p>
{% endfor %}

六、ORM操作

在大型的web开发中,我们肯定会用到数据库操作,那么FastAPI也支持数据库的开发,你可以用 PostgreSQL、MySQL、 SQLite Oracle 等。本文用SQLite为例。我们看下在fastapi是如何操作设计数据库的。

fastapi是一个很优秀的框架,但是缺少一个合适的orm,官方代码里面使用的是sqlalchemy,Tortoise ORM 是受 Django 启发的易于使用的异步 ORM (对象关系映射器)。

在这里插入图片描述

Tortoise ORM文档

Tortoise ORM 目前支持以下数据库:

  • PostgreSQL >= 9.4(使用asyncpg
  • SQLite(使用aiosqlite
  • MySQL/MariaDB(使用aiomysql或使用asyncmy)

6.1、创建模型

以选课系统为例:

models.py

from tortoise.models import Model
from tortoise import fields


class Clas(Model):
    name = fields.CharField(max_length=255, description='班级名称')


class Teacher(Model):
    id = fields.IntField(pk=True)
    name = fields.CharField(max_length=255, description='姓名')
    tno = fields.IntField(description='账号')
    pwd = fields.CharField(max_length=255, description='密码')


class Student(Model):
    id = fields.IntField(pk=True)
    sno = fields.IntField(description='学号')
    pwd = fields.CharField(max_length=255, description='密码')
    name = fields.CharField(max_length=255, description='姓名')
    # 一对多
    clas = fields.ForeignKeyField('models.Clas', related_name='students')
    # 多对多
    courses = fields.ManyToManyField('models.Course', related_name='students',description='学生选课表')


class Course(Model):
    id = fields.IntField(pk=True)
    name = fields.CharField(max_length=255, description='课程名')
    teacher = fields.ForeignKeyField('models.Teacher', related_name='courses', description='课程讲师')

6.2、aerich迁移工具

main.py

import uvicorn
from fastapi import FastAPI
from tortoise.contrib.fastapi import register_tortoise
from settings import TORTOISE_ORM


app = FastAPI()

# 该方法会在fastapi启动时触发,内部通过传递进去的app对象,监听服务启动和终止事件
# 当检测到启动事件时,会初始化Tortoise对象,如果generate_schemas为True则还会进行数据库迁移
# 当检测到终止事件时,会关闭连接
register_tortoise(
    app,
    config=TORTOISE_ORM,
    # generate_schemas=True,  # 如果数据库为空,则自动生成对应表单,生产环境不要开
    # add_exception_handlers=True,  # 生产环境不要开,会泄露调试信息
)

if __name__ == '__main__':
    uvicorn.run('main:app', host='127.0.0.1', port=8000, reload=True,
                debug=True, workers=1)

settings.py

TORTOISE_ORM = {
    'connections': {
        'default': {
            # 'engine': 'tortoise.backends.asyncpg',  PostgreSQL
            'engine': 'tortoise.backends.mysql',  # MySQL or Mariadb
            'credentials': {
                'host': '127.0.0.1',
                'port': '3306',
                'user': 'root',
                'password': 'yuan0316',
                'database': 'fastapi',
                'minsize': 1,
                'maxsize': 5,
                'charset': 'utf8mb4',
                "echo": True
            }
        },
    },
    'apps': {
        'models': {
            'models': ['apps.models', "aerich.models"],
            'default_connection': 'default',

        }
    },
    'use_tz': False,
    'timezone': 'Asia/Shanghai'
}

aerich是一种ORM迁移工具,需要结合tortoise异步orm框架使用。安装aerich

pip install aerich
1. 初始化配置,只需要使用一次
aerich init -t settings.TORTOISE_ORM # TORTOISE_ORM配置的位置)

初始化完会在当前目录生成一个文件:pyproject.toml和一个文件夹:migrations

  • pyproject.toml:保存配置文件路径,低版本可能是aerich.ini
  • migrations:存放迁移文件

在这里插入图片描述

2. 初始化数据库,一般情况下只用一次
aerich init-db
  1. 此时数据库中就有相应的表格

  2. 如果TORTOISE_ORM配置文件中的models改了名,则执行这条命令时需要增加--app参数,来指定你修改的名字

在这里插入图片描述

3. 更新模型并进行迁移

修改model类,重新生成迁移文件,比如添加一个字段

class Admin(Model):
    ...
    xxx = fields.CharField(max_length=255)
aerich migrate [--name] (标记修改操作) #  aerich migrate --name add_column

迁移文件名的格式为 {version_num}{datetime}{name|update}.json。

在这里插入图片描述

注意,此时sql并没有执行,数据库中admin表中没有xxx字段

在这里插入图片描述

4. 重新执行迁移,写入数据库
aerich upgrade

在这里插入图片描述

5. 回到上一个版本
aerich downgrade

在这里插入图片描述

6. 查看历史迁移记录
aerich history

在这里插入图片描述

6.3、api接口与restful规范

api接口

应用程序编程接口(Application Programming Interface,API接口),就是应用程序对外提供了一个操作数据的入口,这个入口可以是一个函数或类方法,也可以是一个url地址或者一个网络地址。当客户端调用这个入口,应用程序则会执行对应代码操作,给客户端完成相对应的功能。

在这里插入图片描述

当然,api接口在工作中是比较常见的开发内容,有时候,我们会调用其他人编写的api接口,有时候,我们也需要提供api接口给其他人操作。由此就会带来一个问题,api接口往往都是一个函数、类方法、或者url或其他网络地址,不断是哪一种,当api接口编写过程中,我们都要考虑一个问题就是这个接口应该怎么编写?接口怎么写的更加容易维护和清晰,这就需要大家在调用或者编写api接口的时候要有一个明确的编写规范!!!

restful规范

为了在团队内部形成共识、防止个人习惯差异引起的混乱,我们都需要找到一种大家都觉得很好的接口实现规范,而且这种规范能够让后端写的接口,用途一目了然,减少客户端和服务端双方之间的合作成本。

目前市面上大部分公司开发人员使用的接口实现规范主要有:restful、RPC。

REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”或“表现层状态转化”。

简单来说,REST的含义就是客户端与Web服务器之间进行交互的时候,使用HTTP协议中的4个请求方法代表不同的动作。

GET用来获取资源

POST用来新建资源

PUT用来更新资源

DELETE用来删除资源。

只要API程序遵循了REST风格,那就可以称其为RESTful API。目前在前后端分离的架构中,前后端基本都是通过RESTful API来进行交互。

例如,我们现在要编写一个选课系统的接口,我们可以查询对一个学生进行查询、创建、更新和删除等操作,我们在编写程序的时候就要设计客户端浏览器与我们Web服务端交互的方式和路径。

而对于数据资源分别使用POST、DELETE、GET、UPDATE等请求动作来表达对数据的增删查改。

GET/students获取所有学生
请求方法请求地址后端操作
GET/students获取所有学生
POST/students增加学生
GET/students/1获取编号为1的学生
PUT/students/1修改编号为1的学生
DELETE/students/1删除编号为1的学生

6.4、选课系统接口开发

api/student.py

from fastapi.exceptions import HTTPException

from models import *

from pydantic import BaseModel
from typing import List, Union
from fastapi import APIRouter

api_student = APIRouter()


@api_student.get("/student")
async def getAllStudent():
    students = await Student.all().values("name", "clas__name")
    # students = await Student.filter(name__icontains='a').values("name", "clas__name")
    # print("students", students)
    # for i in students:
    #     print(i)
    #
    # rain = await Student.get(name='rain')
    # print(rain, type(rain))
    # print(rain.sno)

    return students


class StudentModel(BaseModel):
    name: str
    pwd: str
    sno: int
    clas_id: Union[int, None] = None
    courses: List[int] = []


@api_student.post("/student")
async def addStudent(stu: StudentModel):
    # 添加数据库操作
    # 方式1
    # student = Student(name=stu.name, pwd=stu.pwd, sno=stu.sno, clas_id=stu.clas)
    # await student.save()
    # 方式2
    student = await Student.create(name=stu.name, pwd=stu.pwd, sno=stu.sno, clas_id=stu.clas_id)
    print(student, dir(student))

    # 添加多对多关系记录
    courses = await Course.filter(id__in=stu.courses)
    print("courses", courses)
    await student.courses.add(*courses)
    print("student", student.courses)

    return student


@api_student.put("/student/{student_id}")
async def update_student(student_id: int, student: StudentModel):
    data = student.dict(exclude_unset=True)
    courses = data.pop("courses")
    print(data, courses)
    await Student.filter(id=student_id).update(**data)

    courses = await Course.filter(id__in=student.courses)
    edit_student = await Student.get(id=student_id)
    await edit_student.courses.clear()
    await edit_student.courses.add(*courses)

    return student


@api_student.delete("/student/{student_id}")
async def delete_student(student_id: int):
    deleted_count = await Student.filter(id=student_id).delete()  # 条件删除
    if not deleted_count:
        raise HTTPException(status_code=404, detail=f"Student {student_id} not found")
    return {}

七、中间件与CORS

7.1、中间件

你可以向 FastAPI 应用添加中间件.

"中间件"是一个函数,它在每个请求被特定的路径操作处理之前,以及在每个响应之后工作.

在这里插入图片描述

如果你使用了 yield 关键字依赖, 依赖中的退出代码将在执行中间件执行.

如果有任何后台任务(稍后记录), 它们将在执行中间件运行.

要创建中间件你可以在函数的顶部使用装饰器 @app.middleware("http").

中间件参数接收如下参数:

  • request.
  • 一个函数call_next,它将接收request,作为参数.
    • 这个函数将 request 传递给相应的 路径操作.
    • 然后它将返回由相应的路径操作生成的 response.
  • 然后你可以在返回 response 前进一步修改它.
import uvicorn
from fastapi import FastAPI

from fastapi import Request
from fastapi.responses import Response
import time

app = FastAPI()


@app.middleware("http")
async def m2(request: Request, call_next):
    # 请求代码块
    print("m2 request")
    response = await call_next(request)
    # 响应代码块
    response.headers["author"] = "yuan"
    print("m2 response")
    return response


@app.middleware("http")
async def m1(request: Request, call_next):
    # 请求代码块
    print("m1 request")
    # if request.client.host in ["127.0.0.1", ]:  # 黑名单
    #     return Response(content="visit forbidden")

    # if request.url.path in ["/user"]:
    #     return Response(content="visit forbidden")

    start = time.time()

    response = await call_next(request)
    # 响应代码块
    print("m1 response")
    end = time.time()
    response.headers["ProcessTimer"] = str(end - start)
    return response


@app.get("/user")
def get_user():
    time.sleep(3)
    print("get_user函数执行")
    return {
        "user": "current user"
    }


@app.get("/item/{item_id}")
def get_item(item_id: int):
    time.sleep(2)
    print("get_item函数执行")
    return {
        "item_id": item_id
    }


if __name__ == '__main__':
    uvicorn.run('main:app', host='127.0.0.1', port=8030, reload=True,
                debug=True, workers=1)

7.2、CORS

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>Title</title>
   <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
</head>
<body>

<p>click</p>

<script>
 
   $("p").click(function () {
       $.ajax({
           url: "http://127.0.0.1:8080/",
           success: function (res) {
               $("p").html(res.message)
           },
       })
   })

</script>
</body>
</html>
@app.middleware("http")
async def CORSMiddleware(request: Request, call_next):
    response = await call_next(request)
    print(response.headers)
    return response
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()
origins = [
    "http://localhost:63342"
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,  # *:代表所有客户端
    allow_credentials=True,
    allow_methods=["GET"],
    allow_headers=["*"],
)

@app.get("/")
def main():
    return {"message": "Hello World"}


if __name__ == '__main__':
    import uvicorn

    uvicorn.run("main:app", host="127.0.0.1", port=8080, debug=True, reload=True)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2055915.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

百元蓝牙耳机哪个品牌性价比最高?四大高质量耳机爆肝推荐

蓝牙耳机的日常使用非常简便&#xff0c;而且充电也方便。但如今市场中的蓝牙耳机有的质量与价格不匹配&#xff0c;耳机的使用体验不佳&#xff0c;那百元蓝牙耳机哪个品牌性价比最高&#xff1f;关于这一点&#xff0c;作为资深的蓝牙耳机测评师&#xff0c;下面就给大家带来…

2024年下半年软考备考建议

备考建议 第一轮(建议5-10天) 1、了解考试的基本情况&#xff0c;确定是自学还是报班&#xff0c;准备好备考工具; 2、过一下官方教材蓝皮书&#xff0c;借助思维导图对考试科目知识体系结构有大致了解。 第二轮(建议60-80天) 1、按照学习打卡表&#xff0c;一步步学习科目的考…

金贝E-KA1M 5.5T卓越性能,引领行业新高度

金贝 E-KA1M 5.5t 主要适用于家庭、书房、办公室等对噪音有一定要求的环境。它在运行时噪音极低&#xff0c;不会打扰您的日常生活&#xff0c;无论是放在家中还是办公场所&#xff0c;都能悄然为您创造财富。 金贝 E-KA1M 5.5t是一款具有较强算力的静音挖kuang机&#xff0c;其…

Awesome-Chinese-LLM:收集和梳理中文LLM相关的开源模型、应用、数据集及教程等资料

自ChatGPT为代表的大语言模型&#xff08;Large Language Model, LLM&#xff09;出现以后&#xff0c;由于其惊人的类通用人工智能&#xff08;AGI&#xff09;的能力&#xff0c;掀起了新一轮自然语言处理领域的研究和应用的浪潮。尤其是以ChatGLM、LLaMA等平民玩家都能跑起来…

《图解设计模式》笔记(三)生成实例

五、Singleton模式&#xff1a;只有一个实例 Singleton 是指只含有一个元素的集合。因为本模式只能生成一个实例&#xff0c;因此以 Singleton命名。 示例程序类图 Singleton.java public class Singleton {private static Singleton singleton new Singleton();private Si…

达林顿管阵列ULN2003的用途就是非门(输入和输出的关系)

对于UL2003来说&#xff0c;可以看作是非门。 输入为1&#xff0c;输出为0&#xff1b; 输入为0&#xff0c;输出为高组态[接一个上拉电阻即为1] 下面的可以不用看了&#xff0c;如果你想了解深入一点&#xff0c;可以往下看看。 ULN2003A就是个达林顿管&#xff0c; 一&am…

C++入门——“继承”

一、引入 面相对象的计算机语言有三大特性&#xff1a;“封装”、“继承”、“多态”。今天来讲解一下C的一大重要特性——继承。 通俗理解来讲&#xff0c;继承就和现实生活一样&#xff0c;子辈继承父辈的一些特性&#xff0c;C中的继承也可以这样理解。它允许我们在保持原有…

Windows C++控制台菜单库开发与源码展示

Windows C控制台菜单库 声明&#xff1a;演示视频&#xff1a;一、前言二、具体框架三、源码展示console_screen_set.hframeconsole_screen_frame_base.hconsole_screen_frame_char.hconsole_screen_frame_wchar_t.hconsole_screen_frame.h menuconsole_screen_menu_base.hcons…

html+css 实现hover 凹陷按钮

前言:哈喽,大家好,今天给大家分享html+css 绚丽效果!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 📚一、效果📚二、原理解析💡1.这是一个,hover时凹陷的效果。每个按钮是一个button…

【Android 远程数据库操作】

按正常情况下&#xff0c;前端不应该直接进行远程数据库操作&#xff0c;这不是一个明智的方式&#xff0c;应该是后端提供对应接口来处理&#xff0c;奈何公司各方面原因需要前端这样做。 对此&#xff0c;我对远程数据库操作做了总结&#xff0c;便于自己复盘&#xff0c;同…

机器学习第十四章-概率图模型

目录 14.1 隐马尔可夫模型 14.2马尔科夫随机场 14.3条件随机场 14.4学习与推断 14.4.1变量消去 14.4.2信念传播 14.5近似推断 14.5.1 MCMC采样 14.5.2 变分推断 14.6 话题模型 14.1 隐马尔可夫模型 概率围棋型是一类用图来表达变量相关关系的概率模型.它以图为表示工具…

Transformer(课程笔记)

一&#xff1a;Motivation RNN需要顺序的执行&#xff0c;不利于并行计算。 RNN的变体例如GRU、LSTM等需要依靠注意力机制解决信息瓶颈等问题。 抛弃RNN结构&#xff0c;提出了Transformer结构。 Transformer整体架构 二&#xff1a; 输入层&#xff08;BPE&#xff0c;PE&…

《黑神话:悟空》玩家必看!AMD显卡驱动24.8.1版全力支持!

系统之家于8月20日发出最新报道&#xff0c;AMD发布了最新的24.8.1版本驱动&#xff0c;本次更新增加了《黑神话&#xff1a;悟空》《星球大战&#xff1a;亡命之徒》等游戏的支持&#xff0c;且HYPR Tune支持允许HYPR-RX启用游戏内技术。下面跟随小编一起来看看AMD显卡驱动24.…

Centos7 message日志因dockerd、kubelet、warpdrive、containerd等应用迅速增长

问题&#xff1a;公司服务器在部署一套业务后&#xff0c;message日志记录大量的dockerd、kubelet、warpdrive、containerd应用日志&#xff0c;每天增加2G大小的日志 解决方案&#xff1a; 前期吐槽下&#xff1a;发现某个帖子&#xff0c;需要会员或者花钱才能看&#xff0c…

探索网络安全的深度与广度:挑战、策略与未来展望

一、引言 在当今数字化的时代&#xff0c;网络已经成为社会运转的核心基础设施之一。从个人的日常通信、娱乐到企业的业务运营、国家的关键服务&#xff0c;几乎所有领域都依赖于网络。然而&#xff0c;随着网络的普及和应用的深化&#xff0c;网络安全问题也日益凸显&#xf…

松下弧焊机器人维修 控制柜故障 连接线修复

一、Panasonic焊接机器人控制柜与机器人的接线 机器人的控制箱&#xff0c;一定要配对使用。松下焊接机器人控制柜已经记忆了机器人的绝对原点(机器人位置控制原点)。 二、编码器电缆 (圆形连接器) 1. 接口的插头插座要注意&#xff0c;插头要插到插座中。 2. 用一手握住电缆&a…

网络原理TCP/UDP详解

目录 传输属的几种格式 1.xml&#xff1a;通过成对的标签表示键值对信息。 2.json&#xff1a;当前更主流一点的&#xff0c;网络通信的数据格式 3.yml&#xff08;yaml&#xff09;强制要求数据的组织格式 4.google protobuffer 传输层 1.端口号&#xff1a; UDP协议 …

Vue3 组件管理 12 种神仙写法,灵活使用才能提高效率

SFC 单文件组件 顾名思义&#xff0c;就是一个.vue文件只写一个组件 模板写法 如果这个组件想要在别的组件里使用&#xff0c;就需要在另一个.vue中引入和使用、复用 h函数写法 使用 defineComponent h 去进行组件编写 JSX/TSX写法 使用 defineComponent JSX/TSX 去进行…

【html+css 绚丽Loading】 - 000008 三才虚空灵瞳

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享htmlcss 绚丽Loading&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495…

应用方案 | 降压型DC-DC电源管理电路 D2576介绍

概述 D2576是一款高效降压型DC-DC转换器&#xff0c;固定52KHz开关频率&#xff0c;可以提供最高3A输出电流能力&#xff0c;具有低纹波&#xff0c;出色的线性调整率与负载调整率特点。 D2576内置固定频率振荡器与频率补偿电路&#xff0c;简化了电路设计。PWM控制环路可以调节…