当前位置:   article > 正文

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

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

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

  1. #!/usr/bin/env python
  2. import os
  3. import sys
  4. from contextlib import asynccontextmanager
  5. from datetime import datetime
  6. from typing import List
  7. # python -m pip install --upgrade pip fastapi fastapi-cdn-host 'git+ssh://git@github.com/tortoise/tortoise-orm'
  8. import fastapi_cdn_host
  9. from fastapi import FastAPI, HTTPException, Request
  10. from fastapi.responses import RedirectResponse
  11. from pydantic import BaseModel
  12. from tortoise import fields, models
  13. from tortoise.contrib.fastapi import HTTPNotFoundError, RegisterTortoise
  14. from tortoise.contrib.pydantic import pydantic_model_creator
  15. __version__ = "1.0.0"
  16. class Users(models.Model):
  17. """
  18. The User model
  19. """
  20. id = fields.IntField(pk=True)
  21. #: This is a username
  22. username = fields.CharField(max_length=20, unique=True)
  23. name = fields.CharField(max_length=50, null=True)
  24. family_name = fields.CharField(max_length=50, null=True)
  25. category = fields.CharField(max_length=30, default="misc")
  26. password_hash = fields.CharField(max_length=128, null=True)
  27. created_at = fields.DatetimeField(auto_now_add=True)
  28. modified_at = fields.DatetimeField(auto_now=True)
  29. def full_name(self) -> str:
  30. """
  31. Returns the best name
  32. """
  33. if self.name or self.family_name:
  34. return f"{self.name or ''} {self.family_name or ''}".strip()
  35. return self.username
  36. class PydanticMeta:
  37. computed = ["full_name"]
  38. exclude = ["password_hash"]
  39. User_Pydantic = pydantic_model_creator(Users, name="User")
  40. UserIn_Pydantic = pydantic_model_creator(Users, name="UserIn", exclude_readonly=True)
  41. @asynccontextmanager
  42. async def lifespan(app: FastAPI):
  43. # 注册数据库、挂载Redis等……
  44. async with RegisterTortoise(
  45. app,
  46. db_url="sqlite://:memory:",
  47. modules={"models": ["__main__"]},
  48. generate_schemas=True,
  49. add_exception_handlers=True,
  50. ):
  51. yield
  52. app = FastAPI(title="FastAPI CDN Host Demo", lifespan=lifespan, version=__version__)
  53. fastapi_cdn_host.patch_docs(app)
  54. @app.get("/", include_in_schema=False)
  55. async def root(request: Request) -> RedirectResponse:
  56. """首页直接跳转到文档"""
  57. return RedirectResponse("/docs")
  58. @app.get("/robots.txt", include_in_schema=False)
  59. async def robots_txt():
  60. """声明不允许爬虫访问"""
  61. return """
  62. User-agent: *
  63. Disallow: /
  64. """
  65. @app.get("/app")
  66. async def app_info(request: Request) -> dict[str, str | dict | datetime | None]:
  67. """展示客户端IP、服务器时间等信息"""
  68. headers = dict(request.headers)
  69. ip = getattr(request.client, "host", "")
  70. url = request.url
  71. return {
  72. "your ip": ip,
  73. "version": __version__,
  74. "now": datetime.now(),
  75. "headers": headers,
  76. "path": request.url.path,
  77. "url": {"scheme": url.scheme, "hostname": url.hostname, "port": url.port},
  78. }
  79. class Status(BaseModel):
  80. message: str
  81. @app.get("/users", response_model=List[User_Pydantic])
  82. async def get_users():
  83. return await User_Pydantic.from_queryset(Users.all())
  84. @app.post("/users", response_model=User_Pydantic)
  85. async def create_user(user: UserIn_Pydantic):
  86. user_obj = await Users.create(**user.dict(exclude_unset=True))
  87. return await User_Pydantic.from_tortoise_orm(user_obj)
  88. @app.get(
  89. "/user/{user_id}",
  90. response_model=User_Pydantic,
  91. responses={404: {"model": HTTPNotFoundError}},
  92. )
  93. async def get_user(user_id: int):
  94. return await User_Pydantic.from_queryset_single(Users.get(id=user_id))
  95. @app.put(
  96. "/user/{user_id}",
  97. response_model=User_Pydantic,
  98. responses={404: {"model": HTTPNotFoundError}},
  99. )
  100. async def update_user(user_id: int, user: UserIn_Pydantic):
  101. await Users.filter(id=user_id).update(**user.model_dump(exclude_unset=True))
  102. return await User_Pydantic.from_queryset_single(Users.get(id=user_id))
  103. @app.delete(
  104. "/user/{user_id}",
  105. response_model=Status,
  106. responses={404: {"model": HTTPNotFoundError}},
  107. )
  108. async def delete_user(user_id: int):
  109. deleted_count = await Users.filter(id=user_id).delete()
  110. if not deleted_count:
  111. raise HTTPException(status_code=404, detail=f"User {user_id} not found")
  112. return Status(message=f"Deleted user {user_id}")
  113. def _runserver() -> int:
  114. """This is for debug mode to start server. For prod, use supervisor+gunicorn instead."""
  115. return os.system("fastapi dev")
  116. if __name__ == "__main__": # pragma: no cover
  117. sys.exit(_runserver())

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

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

报错如下:

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

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

  1. from typing import TYPE_CHECKING
  2. if TYPE_CHECKING:
  3. from tortoise.contrib.pydantic import PydanticModel
  4. class UserIn_Pydantic(Users, PydanticModel): # type:ignore[misc]
  5. pass
  6. class User_Pydantic(Users, PydanticModel): # type:ignore[misc]
  7. pass
  8. else:
  9. User_Pydantic = pydantic_model_creator(Users, name="User")
  10. UserIn_Pydantic = pydantic_model_creator(
  11. Users, name="UserIn", exclude_readonly=True
  12. )

修改后的完整文件如下:

  1. #!/usr/bin/env python
  2. import os
  3. import sys
  4. from contextlib import asynccontextmanager
  5. from datetime import datetime
  6. from typing import TYPE_CHECKING, List
  7. # python -m pip install --upgrade pip fastapi fastapi-cdn-host 'git+ssh://git@github.com/tortoise/tortoise-orm'
  8. import fastapi_cdn_host
  9. from fastapi import FastAPI, HTTPException, Request
  10. from fastapi.responses import RedirectResponse
  11. from pydantic import BaseModel
  12. from tortoise import fields, models
  13. from tortoise.contrib.fastapi import HTTPNotFoundError, RegisterTortoise
  14. from tortoise.contrib.pydantic import pydantic_model_creator
  15. __version__ = "1.0.0"
  16. class Users(models.Model):
  17. """
  18. The User model
  19. """
  20. id = fields.IntField(pk=True)
  21. #: This is a username
  22. username = fields.CharField(max_length=20, unique=True)
  23. name = fields.CharField(max_length=50, null=True)
  24. family_name = fields.CharField(max_length=50, null=True)
  25. category = fields.CharField(max_length=30, default="misc")
  26. password_hash = fields.CharField(max_length=128, null=True)
  27. created_at = fields.DatetimeField(auto_now_add=True)
  28. modified_at = fields.DatetimeField(auto_now=True)
  29. def full_name(self) -> str:
  30. """
  31. Returns the best name
  32. """
  33. if self.name or self.family_name:
  34. return f"{self.name or ''} {self.family_name or ''}".strip()
  35. return self.username
  36. class PydanticMeta:
  37. computed = ["full_name"]
  38. exclude = ["password_hash"]
  39. if TYPE_CHECKING:
  40. from tortoise.contrib.pydantic import PydanticModel
  41. class UserIn_Pydantic(Users, PydanticModel): # type:ignore[misc]
  42. pass
  43. class User_Pydantic(Users, PydanticModel): # type:ignore[misc]
  44. pass
  45. else:
  46. User_Pydantic = pydantic_model_creator(Users, name="User")
  47. UserIn_Pydantic = pydantic_model_creator(
  48. Users, name="UserIn", exclude_readonly=True
  49. )
  50. @asynccontextmanager
  51. async def lifespan(app: FastAPI):
  52. # 注册数据库、挂载Redis等……
  53. async with RegisterTortoise(
  54. app,
  55. db_url="sqlite://:memory:",
  56. modules={"models": ["__main__"]},
  57. generate_schemas=True,
  58. add_exception_handlers=True,
  59. ):
  60. yield
  61. app = FastAPI(title="FastAPI CDN Host Demo", lifespan=lifespan, version=__version__)
  62. fastapi_cdn_host.patch_docs(app)
  63. @app.get("/", include_in_schema=False)
  64. async def root(request: Request) -> RedirectResponse:
  65. """首页直接跳转到文档"""
  66. return RedirectResponse("/docs")
  67. @app.get("/robots.txt", include_in_schema=False)
  68. async def robots_txt():
  69. """声明不允许爬虫访问"""
  70. return """
  71. User-agent: *
  72. Disallow: /
  73. """
  74. @app.get("/app")
  75. async def app_info(request: Request) -> dict[str, str | dict | datetime | None]:
  76. """展示客户端IP、服务器时间等信息"""
  77. headers = dict(request.headers)
  78. ip = getattr(request.client, "host", "")
  79. url = request.url
  80. return {
  81. "your ip": ip,
  82. "version": __version__,
  83. "now": datetime.now(),
  84. "headers": headers,
  85. "path": request.url.path,
  86. "url": {"scheme": url.scheme, "hostname": url.hostname, "port": url.port},
  87. }
  88. class Status(BaseModel):
  89. message: str
  90. @app.get("/users", response_model=List[User_Pydantic])
  91. async def get_users():
  92. return await User_Pydantic.from_queryset(Users.all())
  93. @app.post("/users", response_model=User_Pydantic)
  94. async def create_user(user: UserIn_Pydantic):
  95. user_obj = await Users.create(**user.dict(exclude_unset=True))
  96. return await User_Pydantic.from_tortoise_orm(user_obj)
  97. @app.get(
  98. "/user/{user_id}",
  99. response_model=User_Pydantic,
  100. responses={404: {"model": HTTPNotFoundError}},
  101. )
  102. async def get_user(user_id: int):
  103. return await User_Pydantic.from_queryset_single(Users.get(id=user_id))
  104. @app.put(
  105. "/user/{user_id}",
  106. response_model=User_Pydantic,
  107. responses={404: {"model": HTTPNotFoundError}},
  108. )
  109. async def update_user(user_id: int, user: UserIn_Pydantic):
  110. await Users.filter(id=user_id).update(**user.model_dump(exclude_unset=True))
  111. return await User_Pydantic.from_queryset_single(Users.get(id=user_id))
  112. @app.delete(
  113. "/user/{user_id}",
  114. response_model=Status,
  115. responses={404: {"model": HTTPNotFoundError}},
  116. )
  117. async def delete_user(user_id: int):
  118. deleted_count = await Users.filter(id=user_id).delete()
  119. if not deleted_count:
  120. raise HTTPException(status_code=404, detail=f"User {user_id} not found")
  121. return Status(message=f"Deleted user {user_id}")
  122. def _runserver() -> int:
  123. """This is for debug mode to start server. For prod, use supervisor+gunicorn instead."""
  124. return os.system("fastapi dev")
  125. if __name__ == "__main__": # pragma: no cover
  126. sys.exit(_runserver())

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/614864
推荐阅读
相关标签
  

闽ICP备14008679号