您的位置:首页 > 房产 > 建筑 > 【fastapi框架:jinja2模板、ORM操作、中间件与CORS】

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

2024/10/6 10:37:03 来源:https://blog.csdn.net/weixin_50556117/article/details/141359249  浏览:    关键词:【fastapi框架:jinja2模板、ORM操作、中间件与CORS】

## 五、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 uvicornapp = 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 fieldsclass 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_ORMapp = 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 HTTPExceptionfrom models import *from pydantic import BaseModel
from typing import List, Union
from fastapi import APIRouterapi_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 studentsclass StudentModel(BaseModel):name: strpwd: strsno: intclas_id: Union[int, None] = Nonecourses: 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()# 方式2student = 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 FastAPIfrom fastapi import Request
from fastapi.responses import Response
import timeapp = 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 CORSMiddlewareapp = 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 uvicornuvicorn.run("main:app", host="127.0.0.1", port=8080, debug=True, reload=True)

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com