FastAPI第四天---数据库与模块化管理

简介: FastAPI第四天---数据库与模块化管理

FastAPI第四天


1. 多应用程序管理


当我们开发的时候,往往会涉及到大量的路由,如果将所有的路由都写在一个文件中,不利于我们对于某个路由以及其处理函数进行修改,更不利于整个项目后期的维护升级。而且一个文件中代码行数过大还会使得开发尤为不便,因此需要将路由进行分文件(模块化)处理。


相信讲到这里,之前有学习过flask框架的应该都有感觉,这不就是flask中的蓝图吗?没错,FastAPI中的APIRouter与蓝图非常相似,同样都是为了分文件编写路由,也都是需要最终到主文件中进行注册。话不多说,借助官网的例子来理解一下。

首先来看看项目的目录结构

image.png


  • main是最终的主文件,也就是FastAPI实例化以及路由注册的地方
  • routersinternal分别是两个路由组文件

为了简便化,像在routers中有两个路由的情况,我们直接把APIRouter实例化在__init__文件中,然后分别引入就好。


items.py


from ..routers import router
from fastapi import HTTPException
data={1:{"name":'aaa'},2:{"name":'bbb'}}
@router.get("/items/",tags=['items'])
async def read_data():
    return data
@router.get("/items/{item_id}",tags=["items"])
async def read_item(item_id:int):
    if item_id not in data:
        raise HTTPException(status_code=404,detail="Item not Found!")
    return {"item_id":item_id,"name":data[item_id]["name"]}
@router.put("/items/{item_id}",tags=["custom"],responses={403:{"description":"Operation forbidden"}})
async def update_item(item_id:int):
    if item_id!=3:
        raise HTTPException(
            status_code=403,
            detail="You can only update the item:3"
        )
    return {"item_id":item_id,"name":"ccc"}
复制代码


users.py


from ..routers import router
@router.get("/users/",tags=["users"])
async def read_users():
    return [
        {"username":"aaa"},
         {"username":"bbb"}
    ]
@router.get("/users/{username}",tags=['users'])
async def read_user(username:str):
    return {"username":username}
复制代码


admin.py


from fastapi import APIRouter
router=APIRouter()
@router.post("/")
async def update_admin():
    return {"message":"Admin change"}
复制代码


main.py


from fastapi import FastAPI
from .routers import items,users
from .internal import admin
app=FastAPI()
app.include_router(users.router)
app.include_router(items.router)
app.include_router(
    admin.router,
    prefix="/admin",
    tags=['admin'],
)
@app.get("/")
async def root():
    return {"message":"hello"}
复制代码


这里稍微总结一下有关的知识

  1. prefix代表前缀,比如prefix=/index,那么后面如果是router.get("aaa")请求的实际上是/index/aaa
  2. tags是标签,也就是Swagger中的名字


image.png


  1. 编写好router后不要忘了使用.include_router()将写好的进行注册


2. 数据库相关


我们除了将数据存储到文件,其实更多的就是将数据存储到数据库中,这样更利于对较大量数据进行管理(增删改查),所以下面就来看看与数据库相关的操作。

首先是数据库连接部分


import sqlalchemy
from databases import Database
DATABASE_URL="sqlite:///./test.db"
database=Database(DATABASE_URL)
sqlalchemy_engine=sqlalchemy.create_engine(DATABASE_URL)
def get_database()->Database:
    return database
复制代码


然后再来定义数据模型


from datetime import datetime
from typing import Optional
import sqlalchemy
from pydantic import BaseModel, Field
class PostBase(BaseModel):
    title: str
    content: str
    publication_date: datetime = Field(default_factory=datetime.now)
class PostPartialUpdate(BaseModel):
    title: Optional[str] = None
    content: Optional[str] = None
class PostCreate(PostBase):
    pass
class PostDB(PostBase):
    id: int
metadata = sqlalchemy.MetaData()
posts=sqlalchemy.Table(
    "posts",
    metadata,
    sqlalchemy.Column("id",sqlalchemy.Integer,primary_key=True,autoincrement=True),
    sqlalchemy.Column("publication_date",sqlalchemy.DateTime(),nullable=False),
    sqlalchemy.Column("title",sqlalchemy.String(length=255),nullable=False),
    sqlalchemy.Column("content",sqlalchemy.Text(),nullable=False),
)
复制代码


首先定义了数据库的Pydantic模型PostBase,然后定义数据库的提交表格式

image.png

这里就类似于创建数据库表的时候定义字段一样,有类型、主键、自增、是否可以为空等等,这部分内容就更偏向于数据库基础,btw过段时间也准备重新学习一遍Mysql。

有了数据库连接以及定义之后,就可以创建增删改查的接口了


from typing import List, Tuple
from databases import Database
from fastapi import Depends, FastAPI, HTTPException, Query, status
from database import get_database, sqlalchemy_engine
from models import (
    metadata,
    posts,
    PostDB,
    PostCreate,
    PostPartialUpdate,
)
app = FastAPI()
@app.on_event("startup")
async def startup():
    await get_database().connect()
    metadata.create_all(sqlalchemy_engine)
@app.on_event("shutdown")
async def shutdown():
    await get_database().disconnect()
async def pagination(
    skip: int = Query(0, ge=0),
    limit: int = Query(10, ge=0),
) -> Tuple[int, int]:
    capped_limit = min(100, limit)
    return (skip, capped_limit)
async def get_post_or_404(
    id: int, database: Database = Depends(get_database)
) -> PostDB:
    select_query = posts.select().where(posts.c.id == id)
    raw_post = await database.fetch_one(select_query)
    if raw_post is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
    return PostDB(**raw_post)
@app.get("/posts")
async def list_posts(
    pagination: Tuple[int, int] = Depends(pagination),
    database: Database = Depends(get_database),
) -> List[PostDB]:
    skip, limit = pagination
    select_query = posts.select().offset(skip).limit(limit)
    rows = await database.fetch_all(select_query)
    results = [PostDB(**row) for row in rows]
    return results
@app.get("/posts/{id}", response_model=PostDB)
async def get_post(post: PostDB = Depends(get_post_or_404)) -> PostDB:
    return post
@app.post("/posts", response_model=PostDB, status_code=status.HTTP_201_CREATED)
async def create_post(
    post: PostCreate, database: Database = Depends(get_database)
) -> PostDB:
    insert_query = posts.insert().values(post.dict())
    post_id = await database.execute(insert_query)
    post_db = await get_post_or_404(post_id, database)
    return post_db
@app.patch("/posts/{id}", response_model=PostDB)
async def update_post(
    post_update: PostPartialUpdate,
    post: PostDB = Depends(get_post_or_404),
    database: Database = Depends(get_database),
) -> PostDB:
    update_query = (
        posts.update()
        .where(posts.c.id == post.id)
        .values(post_update.dict(exclude_unset=True))
    )
    post_id = await database.execute(update_query)
    post_db = await get_post_or_404(post_id, database)
    return post_db
@app.delete("/posts/{id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_post(
    post: PostDB = Depends(get_post_or_404), database: Database = Depends(get_database)
):
    delete_query = posts.delete().where(posts.c.id == post.id)
    await database.execute(delete_query)
复制代码


image.png

image.png

image.png

在开始,我们设置了两个事件

image.png

在我们需要的时候引入依赖函数,得到数据库的连接或者关闭数据库连接。然后在路由中设置增删改查的操作


insert_query = posts.insert().values(post.dict())
post_id = await database.execute(insert_query)
post_db = await get_post_or_404(post_id, database)
复制代码


插入语句就是先设置好插入请求语句,然后执行这条语句。

其他的也是类似,这样我们就能实现基本的数据库增删改查操作。

目录
相关文章
|
7月前
|
安全 JavaScript Java
智慧图书管理|基于SprinBoot+vue的智慧图书管理系统(源码+数据库+文档)
智慧图书管理|基于SprinBoot+vue的智慧图书管理系统(源码+数据库+文档)
65 0
|
4月前
|
安全 关系型数据库 数据库
FastAPI数据库操作秘籍:如何通过高效且安全的数据库访问策略,使你的Web应用飞速运转并保持数据完整性?
【8月更文挑战第31天】在构建现代Web应用时,数据库操作至关重要。FastAPI不仅简化了API创建,还提供了高效数据库交互的方法。本文探讨如何在FastAPI中实现快速、安全的数据处理。FastAPI支持多种数据库,如SQLite、PostgreSQL和MySQL;选择合适的数据库可显著提升性能。通过安装相应驱动并配置连接参数,结合ORM库(如Tortoise-ORM或SQLAlchemy),可以简化数据库操作。使用索引、批量操作及异步处理等最佳实践可进一步提高效率。同时,确保使用参数化查询防止SQL注入,并从环境变量中读取敏感信息以增强安全性。
227 1
|
5月前
|
SQL 自然语言处理 网络协议
【Linux开发实战指南】基于TCP、进程数据结构与SQL数据库:构建在线云词典系统(含注册、登录、查询、历史记录管理功能及源码分享)
TCP(Transmission Control Protocol)连接是互联网上最常用的一种面向连接、可靠的、基于字节流的传输层通信协议。建立TCP连接需要经过著名的“三次握手”过程: 1. SYN(同步序列编号):客户端发送一个SYN包给服务器,并进入SYN_SEND状态,等待服务器确认。 2. SYN-ACK:服务器收到SYN包后,回应一个SYN-ACK(SYN+ACKnowledgment)包,告诉客户端其接收到了请求,并同意建立连接,此时服务器进入SYN_RECV状态。 3. ACK(确认字符):客户端收到服务器的SYN-ACK包后,发送一个ACK包给服务器,确认收到了服务器的确
200 1
|
6月前
|
NoSQL Redis 数据库
docker-compose 自动管理 数据库
docker-compose 自动管理 数据库
198 3
|
7月前
|
存储 数据可视化 关系型数据库
【MySQL进阶之路 | 基础篇】创建和管理数据库
【MySQL进阶之路 | 基础篇】创建和管理数据库
|
7月前
|
JavaScript Java 关系型数据库
租房管理|基于SprinBoot+vue的租房管理系统(源码+数据库+文档)
租房管理|基于SprinBoot+vue的租房管理系统(源码+数据库+文档)
112 0
|
7月前
|
小程序 JavaScript Java
智慧校园|智慧校园管理小程序|基于微信小程序的智慧校园管理系统设计与实现(源码+数据库+文档)
智慧校园|智慧校园管理小程序|基于微信小程序的智慧校园管理系统设计与实现(源码+数据库+文档)
114 0
|
7月前
|
JavaScript 小程序 Java
土家风景文化|基于SSM+vue的土家风景文化管理平台的设计与实现(源码+数据库+文档)
土家风景文化|基于SSM+vue的土家风景文化管理平台的设计与实现(源码+数据库+文档)
56 0
|
7月前
|
JavaScript Java 关系型数据库
木里风景文化|基于SSM+vue的木里风景文化管理平台的设计与实现(源码+数据库+文档)
木里风景文化|基于SSM+vue的木里风景文化管理平台的设计与实现(源码+数据库+文档)
46 0
|
7月前
|
小程序 JavaScript 关系型数据库
实习生管理|基于SprinBoot+vue的微信小程序的实习生管理系统(源码+数据库+文档)
实习生管理|基于SprinBoot+vue的微信小程序的实习生管理系统(源码+数据库+文档)
66 0