tortoise orm的常用方法及示例

模型

  • 导入依赖
from tortoise.models import Model
  • 描述模型

模型示例:

class Tournament(Model):
    id = fields.IntField(primary_key=True)
    name = fields.TextField(description="名称",min_length=2,max_length=50)
    created = fields.DatetimeField(auto_now_add=True)

    def __str__(self):
        return self.name


class Event(Model):
    id = fields.IntField(primary_key=True)
    name = fields.TextField(null=True,index=True)
    tournament = fields.ForeignKeyField('models.Tournament', related_name='events')
    participants = fields.ManyToManyField('models.Team', related_name='events', through='event_team')
    modified = fields.DatetimeField(auto_now=True)
    prize = fields.DecimalField(max_digits=10, decimal_places=2, null=True)

    def __str__(self):
        return self.name


class Team(Model):
    id = fields.IntField(primary_key=True)
    name = fields.TextField()
    code = fields.CharField(null=False, max_length=64, default="", description="编码")

    def __str__(self):
        return self.name

模型 字段内常用标签方法说明:

primary_key:设置为主键 也可以写为 pk=True,默认是False。

null:是否允许字段为空,null=True表示允许为空,程序默认是null=False,即不许为空。

default:设置默认值。

unique:设置是否为唯一字段,unique=True表示该字段在表中唯一,默认是False。

index:设置索引。index=True表示设置该字段为索引,默认是False。

description:设置字段描述内容。

max_length:接收最长长度。

min_length:接收最短长度。

max_digits:针对DecimalField,该字段允许的最大位数(包括整数部分和小数部分的所有数字)。

decimal_places:针对DecimalField,指定该字段的小数位数。

choices:设置预选字段,示例如下:

STATUS_CHOICES = [("active", "Active"), ("inactive", "Inactive")]
status = fields.CharField(max_length=10, choices=STATUS_CHOICES)

auto_now_add=True:仅在创建时自动设置当前时间,之后不可更改,即在调用.create方法或保存新实例的时候,自动设置当前时间。

auto_now=True:每次保存(创建或更新)时都自动设置当前时间。

__str__(self) 方法:定义当对象被转换为字符串时应该返回什么内容.

  • Meta 方法

    Meta用于定义模型的元数据

class Foo(Model):
    ...

    class Meta:
        table="custom_table"
        unique_together=(("field_a", "field_b"), )
        indexes = [("name", "team")]  # 创建 (name, team) 组合索引
        db_table_prefix = "tenant_"  # 所有表名前加上 "tenant_"
        ordering = ["-name"]  # 默认按 name 字段降序排列

table :用于指定数据库中的表名,如果未指定,则默认使用小写化的模型类名作为表名。

table_description:表的描述

unique_together:定义一个或多个字段组合上的唯一约束,确保这些字段组合在表中是唯一的。如果只是需要一个字段是唯一的可以使用字段内标明unique=True。如果是多字段组合唯一,就使用unique_together

indexes:定义一个或多个字段组合上的索引。

ordering :定义默认的排序规则。接受一个由字段名称组成的列表或元组,支持前缀 - 表示降序排列。

abstract :标记模型为抽象基类。抽象基类不会在数据库中创建对应的表,但其字段和方法可以被子类继承。默认是False。

schema:配置表所在的模式名称。

manager:指定 manager 以覆盖默认管理器

  • ForeignKeyField

    用于定义一对多的关系,允许你通过外键字段将一个模型实例关联到另一个模型实例。

    在事件模型中,有定义一个外键引用(完整设定见模型示例)。

     tournament = fields.ForeignKeyField('models.Tournament', related_name='events')
    

    related_name:是一个关键字参数,是自定义从关联模型访问当前模型实例集合的管理器名称,在这个例子中表示Event 模型中的 tournament 字段是一个外键,指向 Tournament 模型。我们为 related_name 设置了 'events'。这意味着对于每一个 Tournament 实例,我们可以使用 .events 来访问与之相关的所有 Event 实例。

    await Tournament.first().prefetch_related("events")
    

    first():从 Tournament 模型中获取第一个记录。

    prefetch_related:预先加载与 Tournament 相关的 events 数据。

  • ManyToManyField

    用于定义多对多的关系,允许两个模型之间的双向关联,并且支持自定义中间表以添加额外信息。

    participants = fields.ManyToManyField('models.Team', related_name='events', through='event_team')
    

模型的方法

简单讲解tortoise orm的增删改查方法以及穿插讲解相对比较常用的其他方法。

  • 创建数据

    all():Python 的内置函数,该函数会遍历这个列表,都为真(即非空、非零、非 None 等)返回True,反之返回False。

    raise HTTPException(...) :FastAPI 中用于抛出 HTTP 异常的一种方式。也可以使用其他类型,比如BaseException等,具体使用方式可以自行查询。

    create :类方法,用一种简洁的方式创建并保存一个新的数据库记录。

#接口定义
@router.post("/add", summary="新增团队")
async def add(name_c: str, code_c: str) -> R:
    await team_service.creat_team(name_c, code_c)
    return R(data={}, success=True)
#实现方法
async def creat_team(name_c: str, code_c: str):
    if not all([name_c, code_c]):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="名称或编码必填")
    await Team.create(name=name_c, code=code_c)
  • 更新数据

    TeamParam: Pydantic 模型,用于定义和验证 API 请求体中的数据。

    @atomic():Tortoise ORM 提供的一个装饰器,用于确保数据库操作在事务中执行。它保证了一系列数据库操作要么全部成功完成,要么全部回滚,不会出现部分成功的情况。

    filter:根据条件获取多个记录,并返回一个查询集。

    update:更新符合条件的所有记录,一般结合filter方法使用。

    exists:检查是否有符合某个条件的记录。

    # Pydantic 模型
    class TeamParam(BaseModel):
        name: str = Field(..., description="团队名称", min_length=1)
        code: str = Field(..., description="团队编码", min_length=1)
    #定义接口
    @router.post("/edit", summary="编辑团队")
    async def edit(role: TeamParam) -> R:
        await team_service.edit(role)
        return R(data={}, success=True)
    #实现方法
    @atomic()
    async def edit(team: TeamParam):
        # 检查角色是否存在
        if not await Team.filter(id=team.id).exists():
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="角色不存在或已被删除")
        if not all([team.name, team.code]):
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="名称或编码必填")
        # 更新角色信息
        await Team.filter(id=team.id).update(
            name=team.name,
            code=team.code
        )
    
  • 删除数据

    id__in:特殊的查询操作符,表示“包含在”或“属于”。这里的 id 是模型中的字段名,而 __in 是操作符,用来指定该字段的值必须存在于提供的列表中。(双下划线)

    delete:从数据库中删除实例。

    #模型定义
    class BatchRemoveParam(BatchOperatorParam):
        """批量删除参数"""
        pass
    #接口定义
    @router.delete("/delete", summary="删除团队")
    async def delete(param: BatchRemoveParam) -> R:
        await team_service.delete(param)
        return R(data={}, success=True)
    #接口实现
    @atomic()
    async def delete(param: BatchRemoveParam):
        if not await Team.filter(id__in=param.ids).exists():
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="角色不存在或已被删除")
        await Team.filter(id__in=param.ids).delete()
    
  • 查询数据

    prefetch_related:一次性预先加载相关对象,避免了 N+1 查询问题。通常用于处理多对多关系或多级外键关系。

    #返回给前端的视图
    class TeamView(BaseModel):
        def __init__(self, model: TeamDTO = None):
            if model:
                super().__init__(model.__dict__)
            else:
                super().__init__()
        
        id: str = Field(None, description="主键")
        name: str = Field(None, description="名称")
        code: str = Field(None, description="编码")
     #定义接口
    @router.get("/select", summary="查看team列表")
    async def select(name: str|None=None, code: str|None=None) -> R[List[TeamView]]:
        return R(await team_service.page_list(name,code))
    #实现方法
    @atomic()
    async def page_list(name: str, code: str):
        # 列表应该返回名称,编号,和类型,
        # 构建基础查询
        query = TeamModel.filter(is_delete=False)
    
        # 如果提供了 team.name,team.code,则添加过滤条件
        if name is not None:
            query = query.filter(name=name)
        elif code is not None:
            query = query.filter(code=code)
        # 查询数据返回team_view
        return await team_get_data(query,False)
    #假设有一个关联的一对多的菜单
    async def team_get_data(query, single: bool) -> Union[TeamView, List[TeamView]]:
        roles = await query.prefetch_related('link_menus').all()
        # 将结果转换为 DTO 并添加菜单信息
        team_dtos = [
            TeamDTO(
                name=team.name,
                code=team.code,
                type=team.type,
                menus=[MenuDTO(name=m.name, code=m.code) for m in await team.link_menus.all()]
            )
            for team in teams
        ]
        if single:
            if len(team_dtos) > 1:
                return TeamView(model=role_dtos[0])
        return [TeamView(model=team_dto) for team_dto in team_dtos]
    

其他相对常用的方法:

  • exclude() - 创建带有给定排除条件的 QuerySet。

  • all() - 创建不带过滤条件的 QuerySet。

  • annotate() - 创建带有给定注释的 QuerySet。

  • first()-将查询集限制为一个对象,并返回一个对象而不是列表。

  • get()-用于根据条件获取单个对象。

     specific = await Team.get(code="ADMIN_TEAM")
    
  • update_or_create()-尝试根据条件获取记录,如果存在则更新,否则创建新记录。它返回一个元组 (instance, created),其中 created 是一个布尔值,表示是否创建了新记录,instance是模型实例。

  • get_or_create()-尝试根据条件获取记录,如果不存在则创建并返回新记录。它也返回一个元组 (instance, created)

  • count()-返回查询结果的数量,而不实际加载数据。

    count = await Team.filter(type=TeamTypeEnum.ADMIN).count()
    
  • limit()-限制查询返回的结果数量。

    top_roles = await Team.all().limit(5)
    
  • order_by-根据指定字段对查询结果进行排序。可以是升序(默认)或降序(使用 - 前缀)。

    # 按名称升序排序
    sorted_asc = await Team.all().order_by("name")
    
    # 按名称降序排序
    sorted_desc = await Team.all().order_by("-name")
    
    # 按多个字段排序
    multi_sorted = await Team.all().order_by("type", "-created_at")
    
  • group_by-对查询结果进行分组,通常与聚合函数一起使用。

  • offset()-方法用于指定查询结果的起始位置(偏移量),通常与 limit() 结合使用来实现分页功能。它告诉数据库从第几条记录开始返回数据。

    # 分页参数
    page_size = 10  # 每页显示的记录数
    page_number = 2  # 当前页码
    
    # 获取第 2 页的数据(即第 11 到第 20 条记录)
    roles_page = await RoleModel.all().offset(page_size * (page_number - 1)).limit(page_size)
    
  • values-返回包含指定字段值的字典列表,而不是模型实例。values_list()返回元祖列表

    ids_and_names = await Team.all().values("id", "name")
    
  • distinct-去除重复的记录,确保查询结果中的每一行都是唯一的。

  • only-只选择指定的字段,忽略其他字段,返回的是模型实例。

    selected_fields = await Team.all().only("id", "name")
    

filter内部常用的方法:

  • not

  • in - 检查字段的值是否在传入的列表中

    # 查找 ID 在 [1, 2, 3] 中的团队
    teams_in_list = await Team.filter(id__in=[1, 2, 3])
    
  • not_in,使用方法同in

  • gte - 大于或等于传入的值

    # 查找 ID 大于或等于 10 的团队
    teams_gte_10 = await Team.filter(id__gte=10)
    
  • gt - 大于传入的值

  • lte - 小于或等于传入的值

  • lt - 小于传入的值

  • range - 在两个给定值之间

    # 查找 ID 在 5 到 15 之间的团队
    teams_in_range = await Team.filter(id__range=(5, 15))
    
  • isnull - 字段为 null

    # 查找 description 为空的团队
    teams_with_null_desc = await Team.filter(description__isnull=True)
    
  • not_isnull - 字段不为 null

  • contains - 字段包含指定子字符串

    # 查找名称包含 "Red" 的团队
    teams_contains_red = await Team.filter(name__contains="Red")
    
  • icontains - 不区分大小写的 contains

  • startswith - 字段是否以值开头

  • istartswith - 不区分大小写的 startswith

  • endswith - 字段是否以值结尾

  • iendswith - 不区分大小写的 endswith

  • iexact - 不区分大小写的相等

  • search - 全文搜索

    # 使用全文搜索查找名称中包含 "red" 或 "blue" 的团队
    teams_search_red_or_blue = await Team.filter(name__search="red | blue")
    

    过滤日期部分,注意目前只支持 PostgreSQL 和 MySQL,但不支持 SQLite:

    class DatePart(Enum):
        year = "YEAR"
        quarter = "QUARTER"
        month = "MONTH"
        week = "WEEK"
        day = "DAY"
        hour = "HOUR"
        minute = "MINUTE"
        second = "SECOND"
        microsecond = "MICROSECOND"
    
    teams = await Team.filter(created_at__year=2020)
    teams = await Team.filter(created_at__month=12)
    teams = await Team.filter(created_at__day=5)
    
    

内容大多来自官网,也可前往官网

你可能感兴趣的:(python,服务器,开发语言)