当前位置: 首页 > article >正文

Fastapi0.115.6之Tortoise ORM0.23.0基本增删改查大全【亲测可用,仅供参考】

自己在有限的时间里写了一个Fastapi之Tortoise ORM增删改查大全,如有写错的地方还请多多指点。接口功能有可能不全,我会迭代的。主要是方便查找。先放出来一下浏览器的接口文档截图

一、models或ORM数据表文件

from tortoise.models import Model
from tortoise import fields

class P_user(Model):
    p_id = fields.IntField(pk=True)
    p_user = fields.CharField(null=False, unique=True, max_length=12, index=True, description='用户账号')
    p_pass = fields.CharField(null=False, unique=True, max_length=256, description='用户密码')
    role = fields.IntField(null=False, verbose_name="角色")
    permissions = fields.IntField(null=False, verbose_name="权限")
    created_at = fields.DatetimeField(auto_now_add=True, verbose_name="创建日期")
    is_deleted = fields.BooleanField(default=False, verbose_name="软删除")

    class Meta:
        db_table = 'p_user'


class P_Login(Model):
    p_id = fields.IntField(pk=True)
    p_user = fields.CharField(null=False, unique=True, max_length=18, description="用户账号")
    p_pass = fields.CharField(null=False, unique=True, max_length=256, description='用户密码')
    p_pic = fields.CharField(null=False, max_length=256, description="用户头像")
    is_deleted = fields.BooleanField(default=False, verbose_name="软删除")

    class Meta:
        db_table = 'p_login'

二、基础接口案例

import logging
from typing import Optional, List
from fastapi_pagination import Page, add_pagination
from fastapi_pagination.ext.tortoise import paginate as tortoise_paginate
from fastapi import APIRouter, HTTPException, Query, Body, Depends, Request
from fastapi.templating import Jinja2Templates
from pydantic import BaseModel, field_validator
from starlette import status
from tortoise.contrib.fastapi import HTTPNotFoundError
from study_ORM.models import P_user

# 设置日志记录
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

p_user_api = APIRouter()

# 查询所有用户信息
@p_user_api.get("/A", summary="查询所有信息接口(不带分页功能)")
async def get_all_users():
    users = await P_user.all()
    logger.info("全部信息查询: %s", users)
    return {"操作结果": users}

# 查询单个字段信息
@p_user_api.get("/B", summary="查询单个字段里的信息接口(精准搜索)")
async def get_user_by_username(p_user: str):
    user = await P_user.filter(p_user=p_user)
    logger.info("过滤查询: %s", user)
    return {"操作结果": user}

# 根据ID查询用户信息
@p_user_api.get("/C", summary="按ID号进行查询")
async def get_user_by_id(p_id: int):
    user = await P_user.get(p_id=p_id)
    logger.info("get查询: %s", user.p_user)
    return {"操作结果": user}

# 模糊查询
@p_user_api.get("/D", summary="按某个字段进行模糊查询")
async def get_user_by_partial_username(p_user: str):
    users = await P_user.filter(p_user__icontains=p_user)
    logger.info("模糊查询: %s", users)
    return {"操作结果": users}

# 查询两个字段信息
@p_user_api.get("/E", summary="只查询其中两个字段里的所有信息")
async def get_user_values():
    users = await P_user.all().values("permissions", "p_user")
    logger.info("values查询: %s", users)
    return {"操作结果": users}

# 分页查询
class UserOut(BaseModel):
    p_user: str
    p_pass: str
    role: Optional[int] = None
    permissions: Optional[int] = None
    is_deleted: Optional[bool]

@p_user_api.get("/p_users/page", response_model=Page[UserOut], summary="分页接口")
async def get_paginated_users():
    return await tortoise_paginate(P_user)
add_pagination(p_user_api)

# 前后端不分离查询
@p_user_api.get("/index", summary="前后端不分离查询")
async def get_all_users_html(request: Request):
    templates = Jinja2Templates(directory="templates")
    users = await P_user.all()
    return templates.TemplateResponse("index.html", {"request": request, "userinfo": users})

# 用户输入校验模型
class P_userIn(BaseModel):
    p_user: str = Body(..., description="项目名称")
    p_pass: str
    role: Optional[int] = None
    permissions: Optional[int] = None
    is_deleted: Optional[bool] = False

    @field_validator("p_user")
    def check_p_user(cls, v):
        if not v:
            raise ValueError('用户名不能为空')
        if not v.isalnum():
            raise ValueError('用户名只能包含字母和数字')
        if len(v) > 12:
            raise ValueError("用户名太长。最多允许 12 个字符。")
        return v

    @field_validator("p_pass")
    def check_p_pass(cls, v):
        if not v:
            raise ValueError('密码不能为空')
        if not 6 <= len(v) <= 16:
            raise ValueError('您输入的密码必须在6到16个字符之间')
        if not any(c.isupper() for c in v) or not any(c.islower() for c in v) or not any(c.isdigit() for c in v):
            raise ValueError('密码必须包含大小写字母和数字')
        return v

    @field_validator("role")
    def check_p_role(cls, v):
        if v is None:
            raise ValueError('角色不能为空')
        if not isinstance(v, int):
            raise TypeError('角色必须为整数')
        if v < 0:
            raise ValueError('角色不能为负数')
        return v

    @field_validator("permissions")
    def check_p_permissions(cls, v):
        if v is None:
            raise ValueError('权限不能为空')
        if not isinstance(v, int):
            raise TypeError('权限必须为整数')
        if v < 0:
            raise ValueError('权限不能为负数')
        return v

# 添加用户信息
@p_user_api.post("/", summary="信息添加接口")
async def add_user(form_data: P_userIn = Depends()):
    try:
        user = await P_user.create(**form_data.model_dump())
        return user
    except Exception as e:
        logger.error(f"数据库操作失败: {e}")
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"创建用户失败: {e}")

# 查询一条用户信息
@p_user_api.get("/{p_id}", summary="查询一条信息接口")
async def get_one_user(p_id: int):
    user = await P_user.get(p_id=p_id)
    return user

# 更新用户信息
@p_user_api.put("/{p_id}", summary="更新信息接口")
async def update_user(p_id: int, form_data: P_userIn = Depends()):
    data = form_data.model_dump()
    await P_user.filter(p_id=p_id).update(**data)
    return {"操作": f"更新id={p_id}一个用户"}

# 硬删除用户信息
@p_user_api.delete("/{p_id}", summary="硬删除一条信息接口")
async def delete_user(p_id: int):
    deleted_count = await P_user.filter(p_id=p_id).delete()
    if not deleted_count:
        raise HTTPException(status_code=404, detail=f"主键为{p_id}用户不存在")
    return {"操作": f"删除id={p_id}一个用户"}

# 硬删除多条用户信息
@p_user_api.delete("/", summary="硬删除多条信息接口")
async def delete_users(p_ids: List[int] = Query(..., description="要删除的用户ID列表")):
    deleted_count = await P_user.filter(p_id__in=p_ids).delete()
    if deleted_count != len(p_ids):
        existing_ids = await P_user.filter(p_id__in=p_ids).values_list('p_id', flat=True)
        not_found_ids = list(set(p_ids) - set(existing_ids))
        raise HTTPException(status_code=404, detail=f"以下用户ID不存在: {not_found_ids}")
    return {"message": f"成功删除 {deleted_count} 个用户", "deleted_ids": p_ids}

# 软删除用户信息
@p_user_api.delete("/user/{p_id}", summary="软删除一条信息接口")
async def soft_delete_user(p_id: int):
    deleted_count = await P_user.filter(p_id=p_id).update(is_deleted=True)
    if not deleted_count:
        raise HTTPException(status_code=404, detail=f"主键为{p_id}用户不存在")
    return {"操作": f"删除id={p_id}一个用户"}

# 软删除多条用户信息
@p_user_api.delete("/user/", summary="软删除多条信息接口")
async def soft_delete_users(p_ids: List[int] = Query(...)):
    if not p_ids:
        raise HTTPException(status_code=400, detail="请提供要删除的用户 ID 列表")
    deleted_count = await P_user.filter(p_id__in=p_ids).update(is_deleted=True)
    if deleted_count != len(p_ids):
        not_found_ids = list(
            set(p_ids) - set(await P_user.filter(p_id__in=p_ids, is_deleted=True).values_list('p_id', flat=True)))
        if not_found_ids:
            raise HTTPException(status_code=404, detail=f"以下用户 ID 不存在: {not_found_ids}")
    return {"操作": f"成功删除 {deleted_count} 个用户", "删除的ID": p_ids}

三、带文件上传功能接口案例

import os
import uuid
from typing import List

import bcrypt
from fastapi import APIRouter, File, UploadFile, HTTPException, Depends, Query
from pydantic import BaseModel, field_validator
from starlette import status
from tortoise.contrib.fastapi import HTTPNotFoundError
from tortoise.exceptions import IntegrityError

from study_ORM.models import P_Login
from study_ORM.utils import allowed_file, validate_image, save_upload_file

p_login_api = APIRouter()

UPLOAD_DIR = "uploads"
os.makedirs(UPLOAD_DIR, exist_ok=True)


class UploadForm(BaseModel):
    p_user: str
    p_pass: str
    is_deleted: bool = False

    @field_validator("p_user")
    def check_p_user(cls, v):
        if not v:
            raise ValueError('用户名不能为空')
        if not v.isalnum():
            raise ValueError('用户名只能包含字母和数字')
        if not 1 <= len(v) <= 12:
            raise ValueError("用户名长度必须在 1 到 12 个字符之间。")
        return v

    @field_validator("p_pass")
    def check_p_pass(cls, v):
        if not v:
            raise ValueError('密码不能为空')
        if not 6 <= len(v) <= 16:
            raise ValueError('密码长度必须在 6 到 16 个字符之间')
        if not any(c.isupper() for c in v) or not any(c.islower() for c in v) or not any(c.isdigit() for c in v):
            raise ValueError('密码必须包含大小写字母和数字')
        return bcrypt.hashpw(v.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')

    @field_validator("is_deleted")
    def check_is_deleted(cls, v):
        if not isinstance(v, bool):
            raise ValueError("软删除必须是布尔类型")
        return v


@p_login_api.post("/upload/", status_code=status.HTTP_201_CREATED, summary="信息添加接口",
                  description="实现信息添加和图片上传")
async def upload_image(file: UploadFile = File(...), form_data: UploadForm = Depends()):
    if not allowed_file(file.filename):
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="无效的文件类型")

    validate_image(file)

    file_ext = file.filename.rsplit('.', 1)[1].lower()
    unique_filename = str(uuid.uuid4()) + "." + file_ext
    file_path = os.path.join(UPLOAD_DIR, unique_filename)

    try:
        await save_upload_file(file, file_path)

        image = await P_Login.create(
            p_user=form_data.p_user,
            p_pass=form_data.p_pass,
            p_pic=file_path,
            is_deleted=form_data.is_deleted
        )
        return {"id": image.p_id, "path": image.p_pic, "user": image.p_user}
    except IntegrityError as e:
        os.remove(file_path)
        raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=f"用户已存在: {e}")
    except Exception as e:
        if os.path.exists(file_path):
            os.remove(file_path)
        print(f"数据库操作失败: {e}")
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"创建用户失败: {e}")


@p_login_api.delete("/{p_id}", summary="单条信息软删除接口", description="软删除单条信息")
async def delete_p_login(p_id: int):
    updated_count = await P_Login.filter(p_id=p_id).update(is_deleted=True)
    if not updated_count:
        raise HTTPNotFoundError(detail=f"主键为 {p_id} 的用户不存在")
    return {"message": f"成功软删除 id 为 {p_id} 的用户"}


@p_login_api.delete("/", summary="多条信息软删除接口", description="软删除多条信息")
async def delete_multiple_p_users(p_ids: List[int] = Query(...)):
    if not p_ids:
        raise HTTPException(status_code=400, detail="请提供要删除的用户 ID 列表")

    updated_count = await P_Login.filter(p_id__in=p_ids).update(is_deleted=True)
    if updated_count != len(p_ids):
        existing_ids = await P_Login.filter(p_id__in=p_ids).values_list('p_id', flat=True)
        not_found_ids = list(set(p_ids) - set(existing_ids))
        if not_found_ids:
            raise HTTPException(status_code=404, detail=f"以下用户 ID 不存在: {not_found_ids}")

    return {"message": f"成功软删除 {updated_count} 个用户,ID 为 {p_ids}"}


@p_login_api.delete("/users/{p_id}", summary="单条信息硬删除接口", description="硬删除单条信息及关联文件")
async def delete_user(p_id: int):
    user = await P_Login.get_or_none(p_id=p_id)
    if not user:
        raise HTTPNotFoundError

    file_path = user.p_pic
    if file_path and os.path.exists(file_path):
        try:
            os.remove(file_path)
        except OSError as e:
            print(f"删除文件出错: {e}")

    await user.delete()
    return {"message": "用户删除成功"}


@p_login_api.delete("/users/", summary="多条信息硬删除接口", description="硬删除多条信息及关联文件")
async def delete_users(user_ids: List[int] = Query(...)):
    if not user_ids:
        raise HTTPException(status_code=400, detail="未提供用户 ID")

    deleted_count = 0
    for user_id in user_ids:
        user = await P_Login.get_or_none(p_id=user_id)
        if user:
            file_path = user.p_pic
            if file_path and os.path.exists(file_path):
                try:
                    os.remove(file_path)
                except OSError as e:
                    print(f"删除用户 {user_id} 的文件出错: {e}")
            await user.delete()
            deleted_count += 1

    if deleted_count == 0:
        raise HTTPNotFoundError(detail="未找到指定 ID 的用户")

    return {"message": f"成功删除 {deleted_count} 个用户"}


class UploadForm(BaseModel):
    p_user: str
    p_pass: str
    is_deleted: bool = False

    @field_validator("p_user")
    def check_p_user(cls, v):
        if not v:
            raise ValueError('用户名不能为空')
        if not v.isalnum():
            raise ValueError('用户名只能包含字母和数字')
        if not 1 <= len(v) <= 12:
            raise ValueError("用户名长度必须在 1 到 12 个字符之间。")
        return v

    @field_validator("p_pass")
    def check_p_pass(cls, v):
        if not v:
            raise ValueError('密码不能为空')
        if not 6 <= len(v) <= 16:
            raise ValueError('密码长度必须在 6 到 16 个字符之间')
        if not any(c.isupper() for c in v) or not any(c.islower() for c in v) or not any(c.isdigit() for c in v):
            raise ValueError('密码必须包含大小写字母和数字')
        return bcrypt.hashpw(v.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')

    @field_validator("is_deleted")
    def check_is_deleted(cls, v):
        if not isinstance(v, bool):
            raise ValueError("软删除必须是布尔类型")
        return v


@p_login_api.put("/up/{p_id}", status_code=status.HTTP_200_OK, summary="更新信息接口",
                 description="实现信息与头像的更新功能")
async def update_user(p_id: int, file: UploadFile = File(None), form_data: UploadForm = Depends()):
    user = await P_Login.get_or_none(p_id=p_id)
    if not user:
        raise HTTPNotFoundError(detail=f"ID 为 {p_id} 的用户不存在")

    if file:
        if not allowed_file(file.filename):
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="无效的文件类型")
        validate_image(file)

        file_path = user.p_pic  # 获取旧文件路径
        if file_path and os.path.exists(file_path):
            try:
                os.remove(file_path)  # 先删除旧文件
            except OSError as e:
                print(f"删除旧文件出错: {e}")

        file_ext = file.filename.rsplit('.', 1)[1].lower()
        unique_filename = str(uuid.uuid4()) + "." + file_ext
        file_path = os.path.join(UPLOAD_DIR, unique_filename)
        await save_upload_file(file, file_path)
    else:
        file_path = user.p_pic  # 如果没有新文件上传,则保持原路径

    try:
        await P_Login.filter(p_id=p_id).update(
            p_user=form_data.p_user,
            p_pass=form_data.p_pass,
            p_pic=file_path,
            is_deleted=form_data.is_deleted
        )
        return {"message": f"成功更新 ID 为 {p_id} 的用户"}
    except IntegrityError as e:
        if file and os.path.exists(file_path):  # 如果数据库操作失败,并且新文件已保存,则删除新文件
            os.remove(file_path)
        raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=f"更新用户失败: {e}")
    except Exception as e:
        if file and os.path.exists(file_path):  # 如果数据库操作失败,并且新文件已保存,则删除新文件
            os.remove(file_path)
        print(f"数据库操作失败: {e}")
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"更新用户信息失败: {e}")


@p_login_api.get("/A", summary="查询所有信息接口", description="查询数据表里所有信息,不带分页")
async def get_all_users():
    userinfo = await P_Login.all()
    return userinfo


http://www.kler.cn/a/501736.html

相关文章:

  • ctypes对接C/C++函数中char*输出型参数
  • Kutools for Excel 简体中文版 - 官方正版授权
  • 后端技术选型 sa-token校验学习 中 文档学习
  • Realsense相机驱动安装及其ROS通讯配置——机器人抓取系统系列文章(四)
  • 深入探讨 Vue.js 的动态组件渲染与性能优化
  • 前端练习题
  • AIDD-人工智能药物设计-通过组合生物合成产生新的类似物的抗真菌费尔南型三萜多聚类素的生物合成表征
  • AI多模态论文解读:LLaVA-CoT:让视觉语言模型逐步推理
  • uni-app持久化登录简单实现
  • 八、系统托盘与配置面板
  • springmvc前端传参,后端接收
  • StarRocks Awards 2024 年度贡献人物
  • 《解锁鸿蒙系统AI能力,开启智能应用开发新时代》
  • 【GoLand】无法debug 无法运行
  • 【Orca】Orca - Graphlet 和 Orbit 计数算法
  • 《解锁计算机视觉智慧:编程实现图片场景文字描述的开源宝藏》
  • mysql-行溢出处理原理
  • 【HTML+CSS+JS+VUE】web前端教程-21-字体属性
  • 智能化文档开发(DI)
  • el-table 合并单元格
  • Redis解决热key问题
  • sql server cdc漏扫数据
  • C# 虚方法和抽象方法的区别,重写和重载的区别,参数修饰符(ref、out、in、params)--09
  • C语言程序环境和预处理详解
  • 基于element UI el-dropdown打造表格操作列的“更多⌵”上下文关联菜单
  • Transmon