tortoise-orm库静态检查(mypy)报错error: Variable “User_Pydantic“ is not valid as a type [valid-type]

具体代码如下(main.py文件):

#!/usr/bin/env python
import os
import sys
from contextlib import asynccontextmanager
from datetime import datetime
from typing import List

# python -m pip install --upgrade pip fastapi fastapi-cdn-host 'git+ssh://git@github.com/tortoise/tortoise-orm'
import fastapi_cdn_host
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import RedirectResponse
from pydantic import BaseModel
from tortoise import fields, models
from tortoise.contrib.fastapi import HTTPNotFoundError, RegisterTortoise
from tortoise.contrib.pydantic import pydantic_model_creator

__version__ = "1.0.0"


class Users(models.Model):
    """
    The User model
    """

    id = fields.IntField(pk=True)
    #: This is a username
    username = fields.CharField(max_length=20, unique=True)
    name = fields.CharField(max_length=50, null=True)
    family_name = fields.CharField(max_length=50, null=True)
    category = fields.CharField(max_length=30, default="misc")
    password_hash = fields.CharField(max_length=128, null=True)
    created_at = fields.DatetimeField(auto_now_add=True)
    modified_at = fields.DatetimeField(auto_now=True)

    def full_name(self) -> str:
        """
        Returns the best name
        """
        if self.name or self.family_name:
            return f"{self.name or ''} {self.family_name or ''}".strip()
        return self.username

    class PydanticMeta:
        computed = ["full_name"]
        exclude = ["password_hash"]


User_Pydantic = pydantic_model_creator(Users, name="User")
UserIn_Pydantic = pydantic_model_creator(Users, name="UserIn", exclude_readonly=True)


@asynccontextmanager
async def lifespan(app: FastAPI):
    # 注册数据库、挂载Redis等……
    async with RegisterTortoise(
        app,
        db_url="sqlite://:memory:",
        modules={"models": ["__main__"]},
        generate_schemas=True,
        add_exception_handlers=True,
    ):
        yield


app = FastAPI(title="FastAPI CDN Host Demo", lifespan=lifespan, version=__version__)
fastapi_cdn_host.patch_docs(app)


@app.get("/", include_in_schema=False)
async def root(request: Request) -> RedirectResponse:
    """首页直接跳转到文档"""
    return RedirectResponse("/docs")


@app.get("/robots.txt", include_in_schema=False)
async def robots_txt():
    """声明不允许爬虫访问"""
    return """
    User-agent: *

    Disallow: /
    """


@app.get("/app")
async def app_info(request: Request) -> dict[str, str | dict | datetime | None]:
    """展示客户端IP、服务器时间等信息"""
    headers = dict(request.headers)
    ip = getattr(request.client, "host", "")
    url = request.url
    return {
        "your ip": ip,
        "version": __version__,
        "now": datetime.now(),
        "headers": headers,
        "path": request.url.path,
        "url": {"scheme": url.scheme, "hostname": url.hostname, "port": url.port},
    }


class Status(BaseModel):
    message: str


@app.get("/users", response_model=List[User_Pydantic])
async def get_users():
    return await User_Pydantic.from_queryset(Users.all())


@app.post("/users", response_model=User_Pydantic)
async def create_user(user: UserIn_Pydantic):
    user_obj = await Users.create(**user.dict(exclude_unset=True))
    return await User_Pydantic.from_tortoise_orm(user_obj)


@app.get(
    "/user/{user_id}",
    response_model=User_Pydantic,
    responses={404: {"model": HTTPNotFoundError}},
)
async def get_user(user_id: int):
    return await User_Pydantic.from_queryset_single(Users.get(id=user_id))


@app.put(
    "/user/{user_id}",
    response_model=User_Pydantic,
    responses={404: {"model": HTTPNotFoundError}},
)
async def update_user(user_id: int, user: UserIn_Pydantic):
    await Users.filter(id=user_id).update(**user.model_dump(exclude_unset=True))
    return await User_Pydantic.from_queryset_single(Users.get(id=user_id))


@app.delete(
    "/user/{user_id}",
    response_model=Status,
    responses={404: {"model": HTTPNotFoundError}},
)
async def delete_user(user_id: int):
    deleted_count = await Users.filter(id=user_id).delete()
    if not deleted_count:
        raise HTTPException(status_code=404, detail=f"User {user_id} not found")
    return Status(message=f"Deleted user {user_id}")


def _runserver() -> int:
    """This is for debug mode to start server. For prod, use supervisor+gunicorn instead."""
    return os.system("fastapi dev")


if __name__ == "__main__":  # pragma: no cover
    sys.exit(_runserver())

创建虚拟环境、安装依赖、执行格式化检查

python3.12 -m venv venv
source venv/*/activate
python -m pip install --upgrade pip fastapi fastapi-cdn-host 'git+ssh://git@github.com/tortoise/tortoise-orm' 'fast-dev-cli[all]'
fast lint main.py

报错如下:

1 file left unchanged
main.py:105: error: Variable "main.User_Pydantic" is not valid as a type  [valid-type]
main.py:105: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
main.py:111: error: Variable "main.UserIn_Pydantic" is not valid as a type  [valid-type]
main.py:111: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
main.py:112: error: UserIn_Pydantic? has no attribute "dict"  [attr-defined]
main.py:130: error: Variable "main.UserIn_Pydantic" is not valid as a type  [valid-type]
main.py:130: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
main.py:131: error: UserIn_Pydantic? has no attribute "model_dump"  [attr-defined]
Found 5 errors in 1 file (checked 1 source file)

解决方法是在type checking的时候,用class的方式定义xxx_Pydantic:

from typing import TYPE_CHECKING


if TYPE_CHECKING:
    from tortoise.contrib.pydantic import PydanticModel

    class UserIn_Pydantic(Users, PydanticModel):  # type:ignore[misc]
        pass

    class User_Pydantic(Users, PydanticModel):  # type:ignore[misc]
        pass
else:
    User_Pydantic = pydantic_model_creator(Users, name="User")
    UserIn_Pydantic = pydantic_model_creator(
        Users, name="UserIn", exclude_readonly=True
    )

修改后的完整文件如下:

#!/usr/bin/env python
import os
import sys
from contextlib import asynccontextmanager
from datetime import datetime
from typing import TYPE_CHECKING, List

# python -m pip install --upgrade pip fastapi fastapi-cdn-host 'git+ssh://git@github.com/tortoise/tortoise-orm'
import fastapi_cdn_host
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import RedirectResponse
from pydantic import BaseModel
from tortoise import fields, models
from tortoise.contrib.fastapi import HTTPNotFoundError, RegisterTortoise
from tortoise.contrib.pydantic import pydantic_model_creator

__version__ = "1.0.0"


class Users(models.Model):
    """
    The User model
    """

    id = fields.IntField(pk=True)
    #: This is a username
    username = fields.CharField(max_length=20, unique=True)
    name = fields.CharField(max_length=50, null=True)
    family_name = fields.CharField(max_length=50, null=True)
    category = fields.CharField(max_length=30, default="misc")
    password_hash = fields.CharField(max_length=128, null=True)
    created_at = fields.DatetimeField(auto_now_add=True)
    modified_at = fields.DatetimeField(auto_now=True)

    def full_name(self) -> str:
        """
        Returns the best name
        """
        if self.name or self.family_name:
            return f"{self.name or ''} {self.family_name or ''}".strip()
        return self.username

    class PydanticMeta:
        computed = ["full_name"]
        exclude = ["password_hash"]


if TYPE_CHECKING:
    from tortoise.contrib.pydantic import PydanticModel

    class UserIn_Pydantic(Users, PydanticModel):  # type:ignore[misc]
        pass

    class User_Pydantic(Users, PydanticModel):  # type:ignore[misc]
        pass
else:
    User_Pydantic = pydantic_model_creator(Users, name="User")
    UserIn_Pydantic = pydantic_model_creator(
        Users, name="UserIn", exclude_readonly=True
    )


@asynccontextmanager
async def lifespan(app: FastAPI):
    # 注册数据库、挂载Redis等……
    async with RegisterTortoise(
        app,
        db_url="sqlite://:memory:",
        modules={"models": ["__main__"]},
        generate_schemas=True,
        add_exception_handlers=True,
    ):
        yield


app = FastAPI(title="FastAPI CDN Host Demo", lifespan=lifespan, version=__version__)
fastapi_cdn_host.patch_docs(app)


@app.get("/", include_in_schema=False)
async def root(request: Request) -> RedirectResponse:
    """首页直接跳转到文档"""
    return RedirectResponse("/docs")


@app.get("/robots.txt", include_in_schema=False)
async def robots_txt():
    """声明不允许爬虫访问"""
    return """
    User-agent: *

    Disallow: /
    """


@app.get("/app")
async def app_info(request: Request) -> dict[str, str | dict | datetime | None]:
    """展示客户端IP、服务器时间等信息"""
    headers = dict(request.headers)
    ip = getattr(request.client, "host", "")
    url = request.url
    return {
        "your ip": ip,
        "version": __version__,
        "now": datetime.now(),
        "headers": headers,
        "path": request.url.path,
        "url": {"scheme": url.scheme, "hostname": url.hostname, "port": url.port},
    }


class Status(BaseModel):
    message: str


@app.get("/users", response_model=List[User_Pydantic])
async def get_users():
    return await User_Pydantic.from_queryset(Users.all())


@app.post("/users", response_model=User_Pydantic)
async def create_user(user: UserIn_Pydantic):
    user_obj = await Users.create(**user.dict(exclude_unset=True))
    return await User_Pydantic.from_tortoise_orm(user_obj)


@app.get(
    "/user/{user_id}",
    response_model=User_Pydantic,
    responses={404: {"model": HTTPNotFoundError}},
)
async def get_user(user_id: int):
    return await User_Pydantic.from_queryset_single(Users.get(id=user_id))


@app.put(
    "/user/{user_id}",
    response_model=User_Pydantic,
    responses={404: {"model": HTTPNotFoundError}},
)
async def update_user(user_id: int, user: UserIn_Pydantic):
    await Users.filter(id=user_id).update(**user.model_dump(exclude_unset=True))
    return await User_Pydantic.from_queryset_single(Users.get(id=user_id))


@app.delete(
    "/user/{user_id}",
    response_model=Status,
    responses={404: {"model": HTTPNotFoundError}},
)
async def delete_user(user_id: int):
    deleted_count = await Users.filter(id=user_id).delete()
    if not deleted_count:
        raise HTTPException(status_code=404, detail=f"User {user_id} not found")
    return Status(message=f"Deleted user {user_id}")


def _runserver() -> int:
    """This is for debug mode to start server. For prod, use supervisor+gunicorn instead."""
    return os.system("fastapi dev")


if __name__ == "__main__":  # pragma: no cover
    sys.exit(_runserver())

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值