refactor:依赖包升级,fastapi升级到最新版本0.100, pydantic升级到2.0版本,多处代码有重构

This commit is contained in:
ktianc 2023-07-17 15:53:19 +08:00
parent 188fb8de0b
commit 01c28dace4
32 changed files with 375 additions and 371 deletions

View File

@ -11,7 +11,7 @@ from fastapi.security import OAuth2PasswordBearer
""" """
系统版本 系统版本
""" """
VERSION = "1.9.3" VERSION = "1.10.0"
"""安全警告: 不要在生产中打开调试运行!""" """安全警告: 不要在生产中打开调试运行!"""
DEBUG = True DEBUG = True

View File

@ -6,7 +6,7 @@
# @IDE : PyCharm # @IDE : PyCharm
# @desc : 增删改查 # @desc : 增删改查
from typing import List, Any from typing import Any
from aioredis import Redis from aioredis import Redis
from fastapi import UploadFile from fastapi import UploadFile
from sqlalchemy.orm import joinedload from sqlalchemy.orm import joinedload
@ -61,7 +61,7 @@ class UserDal(DalBase):
password = data.telephone[5:12] if settings.DEFAULT_PASSWORD == "0" else settings.DEFAULT_PASSWORD password = data.telephone[5:12] if settings.DEFAULT_PASSWORD == "0" else settings.DEFAULT_PASSWORD
data.password = self.model.get_password_hash(password) data.password = self.model.get_password_hash(password)
data.avatar = data.avatar if data.avatar else settings.DEFAULT_AVATAR data.avatar = data.avatar if data.avatar else settings.DEFAULT_AVATAR
obj = self.model(**data.dict(exclude={'role_ids'})) obj = self.model(**data.model_dump(exclude={'role_ids'}))
if data.role_ids: if data.role_ids:
roles = await RoleDal(self.db).get_datas(limit=0, id=("in", data.role_ids), v_return_objs=True) roles = await RoleDal(self.db).get_datas(limit=0, id=("in", data.role_ids), v_return_objs=True)
for role in roles: for role in roles:
@ -206,7 +206,7 @@ class UserDal(DalBase):
"error_url": im.generate_error_url() "error_url": im.generate_error_url()
} }
async def init_password(self, ids: List[int]): async def init_password(self, ids: list[int]):
""" """
初始化所选用户密码 初始化所选用户密码
将用户密码改为系统默认密码并将初始化密码状态改为false 将用户密码改为系统默认密码并将初始化密码状态改为false
@ -226,7 +226,7 @@ class UserDal(DalBase):
await self.db.flush() await self.db.flush()
return result return result
async def init_password_send_sms(self, ids: List[int], rd: Redis): async def init_password_send_sms(self, ids: list[int], rd: Redis):
""" """
初始化所选用户密码并发送通知短信 初始化所选用户密码并发送通知短信
将用户密码改为系统默认密码并将初始化密码状态改为false 将用户密码改为系统默认密码并将初始化密码状态改为false
@ -248,7 +248,7 @@ class UserDal(DalBase):
user["send_sms_msg"] = e.msg user["send_sms_msg"] = e.msg
return result return result
async def init_password_send_email(self, ids: List[int], rd: Redis): async def init_password_send_email(self, ids: list[int], rd: Redis):
""" """
初始化所选用户密码并发送通知邮件 初始化所选用户密码并发送通知邮件
将用户密码改为系统默认密码并将初始化密码状态改为false 将用户密码改为系统默认密码并将初始化密码状态改为false
@ -301,7 +301,7 @@ class UserDal(DalBase):
await self.flush(user) await self.flush(user)
return True return True
async def delete_datas(self, ids: List[int], v_soft: bool = False, **kwargs): async def delete_datas(self, ids: list[int], v_soft: bool = False, **kwargs):
""" """
删除多个用户软删除 删除多个用户软删除
删除后清空所关联的角色 删除后清空所关联的角色
@ -330,7 +330,7 @@ class RoleDal(DalBase):
v_schema: Any = None v_schema: Any = None
): ):
"""创建数据""" """创建数据"""
obj = self.model(**data.dict(exclude={'menu_ids'})) obj = self.model(**data.model_dump(exclude={'menu_ids'}))
menus = await MenuDal(db=self.db).get_datas(limit=0, id=("in", data.menu_ids), v_return_objs=True) menus = await MenuDal(db=self.db).get_datas(limit=0, id=("in", data.menu_ids), v_return_objs=True)
if data.menu_ids: if data.menu_ids:
for menu in menus: for menu in menus:
@ -370,9 +370,9 @@ class RoleDal(DalBase):
"""获取选择数据,全部数据""" """获取选择数据,全部数据"""
sql = select(self.model) sql = select(self.model)
queryset = await self.db.execute(sql) queryset = await self.db.execute(sql)
return [schemas.RoleSelectOut.from_orm(i).dict() for i in queryset.scalars().all()] return [schemas.RoleSelectOut.model_validate(i).model_dump() for i in queryset.scalars().all()]
async def delete_datas(self, ids: List[int], v_soft: bool = False, **kwargs): async def delete_datas(self, ids: list[int], v_soft: bool = False, **kwargs):
""" """
删除多个角色硬删除 删除多个角色硬删除
如果存在用户关联则无法删除 如果存在用户关联则无法删除
@ -442,7 +442,7 @@ class MenuDal(DalBase):
menus = self.generate_router_tree(datas, roots) menus = self.generate_router_tree(datas, roots)
return self.menus_order(menus) return self.menus_order(menus)
def generate_router_tree(self, menus: List[models.VadminMenu], nodes: filter, name: str = "") -> list: def generate_router_tree(self, menus: list[models.VadminMenu], nodes: filter, name: str = "") -> list:
""" """
生成路由树 生成路由树
@ -452,16 +452,16 @@ class MenuDal(DalBase):
""" """
data = [] data = []
for root in nodes: for root in nodes:
router = schemas.RouterOut.from_orm(root) router = schemas.RouterOut.model_validate(root)
router.name = name + "".join(name.capitalize() for name in router.path.split("/")) router.name = name + "".join(name.capitalize() for name in router.path.split("/"))
router.meta = schemas.Meta(title=root.title, icon=root.icon, hidden=root.hidden, alwaysShow=root.alwaysShow) router.meta = schemas.Meta(title=root.title, icon=root.icon, hidden=root.hidden, alwaysShow=root.alwaysShow)
if root.menu_type == "0": if root.menu_type == "0":
sons = filter(lambda i: i.parent_id == root.id, menus) sons = filter(lambda i: i.parent_id == root.id, menus)
router.children = self.generate_router_tree(menus, sons, router.name) router.children = self.generate_router_tree(menus, sons, router.name)
data.append(router.dict()) data.append(router.model_dump())
return data return data
def generate_tree_list(self, menus: List[models.VadminMenu], nodes: filter) -> list: def generate_tree_list(self, menus: list[models.VadminMenu], nodes: filter) -> list:
""" """
生成菜单树列表 生成菜单树列表
@ -470,14 +470,14 @@ class MenuDal(DalBase):
""" """
data = [] data = []
for root in nodes: for root in nodes:
router = schemas.TreeListOut.from_orm(root) router = schemas.TreeListOut.model_validate(root)
if root.menu_type == "0" or root.menu_type == "1": if root.menu_type == "0" or root.menu_type == "1":
sons = filter(lambda i: i.parent_id == root.id, menus) sons = filter(lambda i: i.parent_id == root.id, menus)
router.children = self.generate_tree_list(menus, sons) router.children = self.generate_tree_list(menus, sons)
data.append(router.dict()) data.append(router.model_dump())
return data return data
def generate_tree_options(self, menus: List[models.VadminMenu], nodes: filter) -> list: def generate_tree_options(self, menus: list[models.VadminMenu], nodes: filter) -> list:
""" """
生成菜单树选择项 生成菜单树选择项
@ -504,7 +504,7 @@ class MenuDal(DalBase):
item[children] = sorted(item[children], key=lambda menu: menu[order]) item[children] = sorted(item[children], key=lambda menu: menu[order])
return result return result
async def delete_datas(self, ids: List[int], v_soft: bool = False, **kwargs): async def delete_datas(self, ids: list[int], v_soft: bool = False, **kwargs):
""" """
删除多个菜单 删除多个菜单
如果存在角色关联则无法删除 如果存在角色关联则无法删除

View File

@ -17,7 +17,14 @@ class RoleParams(QueryParams):
""" """
列表分页 列表分页
""" """
def __init__(self, name: str = None, role_key: str = None, disabled: bool = None, params: Paging = Depends()):
def __init__(
self,
name: str | None = None,
role_key: str | None = None,
disabled: bool | None = None,
params: Paging = Depends()
):
super().__init__(params) super().__init__(params)
self.name = ("like", name) self.name = ("like", name)
self.role_key = ("like", role_key) self.role_key = ("like", role_key)

View File

@ -20,11 +20,11 @@ class UserParams(QueryParams):
def __init__( def __init__(
self, self,
name: str = None, name: str | None = None,
telephone: str = None, telephone: str | None = None,
email: str = None, email: str | None = None,
is_active: bool | str = None, is_active: bool | None = None,
is_staff: bool | str = None, is_staff: bool | None = None,
params: Paging = Depends() params: Paging = Depends()
): ):
super().__init__(params) super().__init__(params)

View File

@ -6,72 +6,60 @@
# @IDE : PyCharm # @IDE : PyCharm
# @desc : pydantic 模型,用于数据库序列化操作 # @desc : pydantic 模型,用于数据库序列化操作
# pydantic 验证数据https://blog.csdn.net/qq_44291044/article/details/104693526
from pydantic import BaseModel, ConfigDict
from typing import Optional, List
from pydantic import BaseModel
from core.data_types import DatetimeStr from core.data_types import DatetimeStr
class Menu(BaseModel): class Menu(BaseModel):
title: str title: str
icon: Optional[str] = None icon: str | None = None
component: Optional[str] = None component: str | None = None
redirect: Optional[str] = None redirect: str | None = None
path: Optional[str] = None path: str | None = None
disabled: bool = False disabled: bool = False
hidden: bool = False hidden: bool = False
order: Optional[int] = None order: int | None = None
perms: Optional[str] = None perms: str | None = None
parent_id: Optional[int] = None parent_id: int | None = None
menu_type: str menu_type: str
alwaysShow: Optional[bool] = True alwaysShow: bool | None = True
class MenuSimpleOut(Menu): class MenuSimpleOut(Menu):
model_config = ConfigDict(from_attributes=True)
id: int id: int
create_datetime: DatetimeStr create_datetime: DatetimeStr
update_datetime: DatetimeStr update_datetime: DatetimeStr
class Config:
orm_mode = True
class Meta(BaseModel): class Meta(BaseModel):
title: str title: str
icon: Optional[str] = None icon: str | None = None
hidden: bool = False hidden: bool = False
noCache: Optional[bool] = False noCache: bool | None = False
breadcrumb: Optional[bool] = True breadcrumb: bool | None = True
affix: Optional[bool] = False affix: bool | None = False
noTagsView: Optional[bool] = False noTagsView: bool | None = False
canTo: Optional[bool] = False canTo: bool | None = False
alwaysShow: Optional[bool] = True alwaysShow: bool | None = True
# 路由展示 # 路由展示
class RouterOut(BaseModel): class RouterOut(BaseModel):
name: Optional[str] = None model_config = ConfigDict(from_attributes=True)
component: Optional[str] = None
name: str | None = None
component: str | None = None
path: str path: str
redirect: Optional[str] = None redirect: str | None = None
meta: Optional[Meta] = None meta: Meta | None = None
order: Optional[int] = None order: int | None = None
children: List['RouterOut'] = [] children: list[dict] = []
class Config:
orm_mode = True
RouterOut.update_forward_refs()
class TreeListOut(MenuSimpleOut): class TreeListOut(MenuSimpleOut):
children: List['TreeListOut'] = [] model_config = ConfigDict(from_attributes=True)
class Config: children: list[dict] = []
orm_mode = True
RouterOut.update_forward_refs()

View File

@ -6,11 +6,8 @@
# @IDE : PyCharm # @IDE : PyCharm
# @desc : pydantic 模型,用于数据库序列化操作 # @desc : pydantic 模型,用于数据库序列化操作
# pydantic 验证数据https://blog.csdn.net/qq_44291044/article/details/104693526
from pydantic import BaseModel, ConfigDict
from typing import Optional, List
from pydantic import BaseModel
from core.data_types import DatetimeStr from core.data_types import DatetimeStr
from .menu import MenuSimpleOut from .menu import MenuSimpleOut
@ -18,36 +15,34 @@ from .menu import MenuSimpleOut
class Role(BaseModel): class Role(BaseModel):
name: str name: str
disabled: bool = False disabled: bool = False
order: Optional[int] = None order: int | None = None
desc: Optional[str] = None desc: str | None = None
role_key: str role_key: str
is_admin: bool = False is_admin: bool = False
class RoleSimpleOut(Role): class RoleSimpleOut(Role):
model_config = ConfigDict(from_attributes=True)
id: int id: int
create_datetime: DatetimeStr create_datetime: DatetimeStr
update_datetime: DatetimeStr update_datetime: DatetimeStr
class Config:
orm_mode = True
class RoleOut(RoleSimpleOut): class RoleOut(RoleSimpleOut):
menus: Optional[List[MenuSimpleOut]] = [] model_config = ConfigDict(from_attributes=True)
class Config: menus: list[MenuSimpleOut] = []
orm_mode = True
class RoleIn(Role): class RoleIn(Role):
menu_ids: Optional[List[int]] = [] menu_ids: list[int] = []
class RoleSelectOut(BaseModel): class RoleSelectOut(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int id: int
name: str name: str
disabled: bool disabled: bool
class Config:
orm_mode = True

View File

@ -6,33 +6,32 @@
# @IDE : PyCharm # @IDE : PyCharm
# @desc : pydantic 模型,用于数据库序列化操作 # @desc : pydantic 模型,用于数据库序列化操作
# pydantic 验证数据https://blog.csdn.net/qq_44291044/article/details/104693526
from pydantic import BaseModel, ConfigDict, field_validator
from pydantic_core.core_schema import FieldValidationInfo
from typing import List, Optional
from pydantic import BaseModel, root_validator
from core.data_types import Telephone, DatetimeStr, Email from core.data_types import Telephone, DatetimeStr, Email
from .role import RoleSimpleOut from .role import RoleSimpleOut
class User(BaseModel): class User(BaseModel):
name: Optional[str] = None name: str | None = None
telephone: Telephone telephone: Telephone
email: Optional[Email] = None email: Email | None = None
nickname: Optional[str] = None nickname: str | None = None
avatar: Optional[str] = None avatar: str | None = None
is_active: Optional[bool] = True is_active: bool | None = True
is_staff: Optional[bool] = True is_staff: bool | None = True
gender: Optional[str] = "0" gender: str | None = "0"
is_wx_server_openid: Optional[bool] = False is_wx_server_openid: bool | None = False
class UserIn(User): class UserIn(User):
""" """
创建用户 创建用户
""" """
role_ids: Optional[List[int]] = [] role_ids: list[int] = []
password: Optional[str] = "" password: str | None = ""
class UserUpdateBaseInfo(BaseModel): class UserUpdateBaseInfo(BaseModel):
@ -41,53 +40,50 @@ class UserUpdateBaseInfo(BaseModel):
""" """
name: str name: str
telephone: Telephone telephone: Telephone
email: Optional[Email] = None email: Email | None = None
nickname: Optional[str] = None nickname: str | None = None
gender: Optional[str] = "0" gender: str | None = "0"
class UserUpdate(User): class UserUpdate(User):
""" """
更新用户详细信息 更新用户详细信息
""" """
name: Optional[str] = None name: str | None = None
telephone: Telephone telephone: Telephone
email: Optional[Email] = None email: Email | None = None
nickname: Optional[str] = None nickname: str | None = None
avatar: Optional[str] = None avatar: str | None = None
is_active: Optional[bool] = True is_active: bool | None = True
is_staff: Optional[bool] = False is_staff: bool | None = False
gender: Optional[str] = "0" gender: str | None = "0"
role_ids: Optional[List[int]] = [] role_ids: list[int] = []
class UserSimpleOut(User): class UserSimpleOut(User):
model_config = ConfigDict(from_attributes=True)
id: int id: int
update_datetime: DatetimeStr update_datetime: DatetimeStr
create_datetime: DatetimeStr create_datetime: DatetimeStr
is_reset_password: Optional[bool] = None is_reset_password: bool | None = None
last_login: Optional[DatetimeStr] = None last_login: DatetimeStr | None = None
last_ip: Optional[str] = None last_ip: str | None = None
class Config:
orm_mode = True
class UserOut(UserSimpleOut): class UserOut(UserSimpleOut):
roles: Optional[List[RoleSimpleOut]] = [] model_config = ConfigDict(from_attributes=True)
class Config: roles: list[RoleSimpleOut] = []
orm_mode = True
class ResetPwd(BaseModel): class ResetPwd(BaseModel):
password: str password: str
password_two: str password_two: str
@root_validator @field_validator('password_two')
def check_passwords_match(cls, values): def check_passwords_match(cls, v, info: FieldValidationInfo):
pw1, pw2 = values.get('password'), values.get('password_two') if 'password' in info.data and v != info.data['password']:
if pw1 is not None and pw2 is not None and pw1 != pw2:
raise ValueError('两次密码不一致!') raise ValueError('两次密码不一致!')
return values return v

View File

@ -5,7 +5,6 @@
# @IDE : PyCharm # @IDE : PyCharm
# @desc : 获取认证后的信息工具 # @desc : 获取认证后的信息工具
from typing import List, Optional
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import joinedload from sqlalchemy.orm import joinedload
from apps.vadmin.auth.crud import UserDal from apps.vadmin.auth.crud import UserDal
@ -76,7 +75,7 @@ class FullAdminAuth(AuthValidation):
如果有权限那么会验证该用户是否包括权限列表中的其中一个权限 如果有权限那么会验证该用户是否包括权限列表中的其中一个权限
""" """
def __init__(self, permissions: Optional[List[str]] = None): def __init__(self, permissions: list[str] | None = None):
if permissions: if permissions:
self.permissions = set(permissions) self.permissions = set(permissions)
else: else:

View File

@ -21,6 +21,7 @@ class Auth(BaseModel):
db: AsyncSession db: AsyncSession
class Config: class Config:
# 接收任意类型
arbitrary_types_allowed = True arbitrary_types_allowed = True
@ -49,7 +50,6 @@ class AuthValidation:
status_code=status.HTTP_403_FORBIDDEN status_code=status.HTTP_403_FORBIDDEN
) )
try: try:
# TODO token解析失败问题解决
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]) payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
telephone: str = payload.get("sub") telephone: str = payload.get("sub")
exp: int = payload.get("exp") exp: int = payload.get("exp")

View File

@ -7,7 +7,7 @@
# @desc : 登录验证装饰器 # @desc : 登录验证装饰器
from fastapi import Request from fastapi import Request
from pydantic import BaseModel, validator from pydantic import BaseModel, validator, field_validator
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from application.settings import DEFAULT_AUTH_ERROR_MAX_NUMBER, DEMO from application.settings import DEFAULT_AUTH_ERROR_MAX_NUMBER, DEMO
from apps.vadmin.auth import crud, schemas from apps.vadmin.auth import crud, schemas
@ -23,21 +23,21 @@ class LoginForm(BaseModel):
method: str = '0' # 认证方式0密码登录1短信登录2微信一键登录 method: str = '0' # 认证方式0密码登录1短信登录2微信一键登录
platform: str = '0' # 登录平台0PC端管理系统1移动端管理系统 platform: str = '0' # 登录平台0PC端管理系统1移动端管理系统
# validators # 重用验证器https://docs.pydantic.dev/dev-v2/usage/validators/#reuse-validators
_normalize_telephone = validator('telephone', allow_reuse=True)(vali_telephone) normalize_telephone = field_validator('telephone')(vali_telephone)
class WXLoginForm(BaseModel): class WXLoginForm(BaseModel):
telephone: Optional[str] = None telephone: str | None = None
code: str code: str
method: str = '2' # 认证方式0密码登录1短信登录2微信一键登录 method: str = '2' # 认证方式0密码登录1短信登录2微信一键登录
platform: str = '1' # 登录平台0PC端管理系统1移动端管理系统 platform: str = '1' # 登录平台0PC端管理系统1移动端管理系统
class LoginResult(BaseModel): class LoginResult(BaseModel):
status: Optional[bool] = False status: bool | None = False
user: Optional[schemas.UserOut] = None user: schemas.UserOut | None = None
msg: Optional[str] = None msg: str | None = None
class Config: class Config:
arbitrary_types_allowed = True arbitrary_types_allowed = True
@ -85,6 +85,6 @@ class LoginValidation:
await count.delete() await count.delete()
self.result.msg = "OK" self.result.msg = "OK"
self.result.status = True self.result.status = True
self.result.user = schemas.UserSimpleOut.from_orm(user) self.result.user = schemas.UserSimpleOut.model_validate(user)
await user.update_login_info(db, request.client.host) await user.update_login_info(db, request.client.host)
return self.result return self.result

View File

@ -87,7 +87,7 @@ async def post_user_current_update_avatar(file: UploadFile, auth: Auth = Depends
@app.get("/user/admin/current/info", summary="获取当前管理员信息") @app.get("/user/admin/current/info", summary="获取当前管理员信息")
async def get_user_admin_current_info(auth: Auth = Depends(FullAdminAuth())): async def get_user_admin_current_info(auth: Auth = Depends(FullAdminAuth())):
result = schemas.UserOut.from_orm(auth.user).dict() result = schemas.UserOut.model_validate(auth.user).model_dump()
result["permissions"] = list(FullAdminAuth.get_user_permissions(auth.user)) result["permissions"] = list(FullAdminAuth.get_user_permissions(auth.user))
return SuccessResponse(result) return SuccessResponse(result)

View File

@ -7,34 +7,32 @@
# @desc : 常见问题 # @desc : 常见问题
from typing import Optional from typing import Optional
from pydantic import BaseModel from pydantic import BaseModel, ConfigDict
from core.data_types import DatetimeStr from core.data_types import DatetimeStr
from apps.vadmin.auth.schemas import UserSimpleOut from apps.vadmin.auth.schemas import UserSimpleOut
from .issue_category import IssueCategorySimpleOut from .issue_category import IssueCategorySimpleOut
class Issue(BaseModel): class Issue(BaseModel):
category_id: Optional[int] = None category_id: int | None = None
create_user_id: Optional[int] = None create_user_id: int | None = None
title: Optional[str] = None title: str | None = None
content: Optional[str] = None content: str | None = None
view_number: Optional[int] = None view_number: int | None = None
is_active: Optional[bool] = None is_active: bool | None = None
class IssueSimpleOut(Issue): class IssueSimpleOut(Issue):
model_config = ConfigDict(from_attributes=True)
id: int id: int
update_datetime: DatetimeStr update_datetime: DatetimeStr
create_datetime: DatetimeStr create_datetime: DatetimeStr
class Config:
orm_mode = True
class IssueListOut(IssueSimpleOut): class IssueListOut(IssueSimpleOut):
model_config = ConfigDict(from_attributes=True)
create_user: UserSimpleOut create_user: UserSimpleOut
category: IssueCategorySimpleOut category: IssueCategorySimpleOut
class Config:
orm_mode = True

View File

@ -8,38 +8,36 @@
from typing import Optional from typing import Optional
from pydantic import BaseModel, Field from pydantic import BaseModel, Field, ConfigDict
from core.data_types import DatetimeStr from core.data_types import DatetimeStr
from apps.vadmin.auth.schemas import UserSimpleOut from apps.vadmin.auth.schemas import UserSimpleOut
class IssueCategory(BaseModel): class IssueCategory(BaseModel):
name: Optional[str] = None name: str | None = None
platform: Optional[str] = None platform: str | None = None
is_active: Optional[bool] = None is_active: bool | None = None
create_user_id: Optional[int] = None create_user_id: int | None = None
class IssueCategorySimpleOut(IssueCategory): class IssueCategorySimpleOut(IssueCategory):
model_config = ConfigDict(from_attributes=True)
id: int id: int
update_datetime: DatetimeStr update_datetime: DatetimeStr
create_datetime: DatetimeStr create_datetime: DatetimeStr
class Config:
orm_mode = True
class IssueCategoryListOut(IssueCategorySimpleOut): class IssueCategoryListOut(IssueCategorySimpleOut):
create_user: UserSimpleOut model_config = ConfigDict(from_attributes=True)
class Config: create_user: UserSimpleOut
orm_mode = True
class IssueCategoryOptionsOut(BaseModel): class IssueCategoryOptionsOut(BaseModel):
model_config = ConfigDict(from_attributes=True)
label: str = Field(alias='name') label: str = Field(alias='name')
value: int = Field(alias='id') value: int = Field(alias='id')
class Config:
orm_mode = True

View File

@ -6,24 +6,22 @@
# @IDE : PyCharm # @IDE : PyCharm
# @desc : 简要说明 # @desc : 简要说明
from typing import Optional, List from pydantic import BaseModel, ConfigDict
from pydantic import BaseModel, Field
from core.data_types import DatetimeStr from core.data_types import DatetimeStr
from .issue import IssueSimpleOut from .issue import IssueSimpleOut
class IssueCategoryPlatformOut(BaseModel): class IssueCategoryPlatformOut(BaseModel):
name: Optional[str] = None model_config = ConfigDict(from_attributes=True)
platform: Optional[str] = None
is_active: Optional[bool] = None name: str | None = None
create_user_id: Optional[int] = None platform: str | None = None
is_active: bool | None = None
create_user_id: int | None = None
id: int id: int
update_datetime: DatetimeStr update_datetime: DatetimeStr
create_datetime: DatetimeStr create_datetime: DatetimeStr
issues: Optional[List[IssueSimpleOut]] = None issues: list[IssueSimpleOut] = None
class Config:
orm_mode = True

View File

@ -6,7 +6,6 @@
# @IDE : PyCharm # @IDE : PyCharm
# @desc : 数据库 增删改查操作 # @desc : 数据库 增删改查操作
import random import random
from typing import List
# sqlalchemy 查询操作https://segmentfault.com/a/1190000016767008 # sqlalchemy 查询操作https://segmentfault.com/a/1190000016767008
# sqlalchemy 关联查询https://www.jianshu.com/p/dfad7c08c57a # sqlalchemy 关联查询https://www.jianshu.com/p/dfad7c08c57a
# sqlalchemy 关联查询详细https://blog.csdn.net/u012324798/article/details/103940527 # sqlalchemy 关联查询详细https://blog.csdn.net/u012324798/article/details/103940527
@ -22,7 +21,7 @@ class LoginRecordDal(DalBase):
def __init__(self, db: AsyncSession): def __init__(self, db: AsyncSession):
super(LoginRecordDal, self).__init__(db, models.VadminLoginRecord, schemas.LoginRecordSimpleOut) super(LoginRecordDal, self).__init__(db, models.VadminLoginRecord, schemas.LoginRecordSimpleOut)
async def get_user_distribute(self) -> List[dict]: async def get_user_distribute(self) -> list[dict]:
""" """
获取用户登录分布情况 获取用户登录分布情况
高德经纬度查询https://lbs.amap.com/tools/picker 高德经纬度查询https://lbs.amap.com/tools/picker

View File

@ -17,8 +17,15 @@ class LoginParams(QueryParams):
""" """
列表分页 列表分页
""" """
def __init__(self, ip: str = None, address: str = None, telephone: str = None, status: bool = None, def __init__(
platform: str = None, params: Paging = Depends()): self,
ip: str = None,
address: str = None,
telephone: str = None,
status: bool = None,
platform: str = None,
params: Paging = Depends()
):
super().__init__(params) super().__init__(params)
self.ip = ("like", ip) self.ip = ("like", ip)
self.telephone = ("like", telephone) self.telephone = ("like", telephone)

View File

@ -17,8 +17,13 @@ class OperationParams(QueryParams):
""" """
列表分页 列表分页
""" """
def __init__(self, summary: str = None, telephone: str = None, request_method: str = None, def __init__(
params: Paging = Depends()): self,
summary: str = None,
telephone: str = None,
request_method: str = None,
params: Paging = Depends()
):
super().__init__(params) super().__init__(params)
self.summary = ("like", summary) self.summary = ("like", summary)
self.telephone = ("like", telephone) self.telephone = ("like", telephone)

View File

@ -6,38 +6,33 @@
# @IDE : PyCharm # @IDE : PyCharm
# @desc : pydantic 模型,用于数据库序列化操作 # @desc : pydantic 模型,用于数据库序列化操作
# pydantic 验证数据https://blog.csdn.net/qq_44291044/article/details/104693526 from pydantic import BaseModel, ConfigDict
from typing import Optional
from pydantic import BaseModel
from core.data_types import DatetimeStr from core.data_types import DatetimeStr
class LoginRecord(BaseModel): class LoginRecord(BaseModel):
telephone: str telephone: str
status: bool status: bool
ip: Optional[str] = None ip: str | None = None
address: Optional[str] = None address: str | None = None
browser: Optional[str] = None browser: str | None = None
system: Optional[str] = None system: str | None = None
response: Optional[str] = None response: str | None = None
request: Optional[str] = None request: str | None = None
postal_code: Optional[str] = None postal_code: str | None = None
area_code: Optional[str] = None area_code: str | None = None
country: Optional[str] = None country: str | None = None
province: Optional[str] = None province: str | None = None
city: Optional[str] = None city: str | None = None
county: Optional[str] = None county: str | None = None
operator: Optional[str] = None operator: str | None = None
platform: Optional[str] = None platform: str | None = None
login_method: Optional[str] = None login_method: str | None = None
class LoginRecordSimpleOut(LoginRecord): class LoginRecordSimpleOut(LoginRecord):
model_config = ConfigDict(from_attributes=True)
id: int id: int
create_datetime: DatetimeStr create_datetime: DatetimeStr
update_datetime: DatetimeStr update_datetime: DatetimeStr
class Config:
orm_mode = True

View File

@ -6,34 +6,30 @@
# @IDE : PyCharm # @IDE : PyCharm
# @desc : pydantic 模型,用于数据库序列化操作 # @desc : pydantic 模型,用于数据库序列化操作
# pydantic 验证数据https://blog.csdn.net/qq_44291044/article/details/104693526
from pydantic import BaseModel, ConfigDict
from typing import Optional, List from core.data_types import DatetimeStr
from pydantic import BaseModel
from core.data_types import DatetimeStr, ObjectIdStr
class OperationRecord(BaseModel): class OperationRecord(BaseModel):
telephone: Optional[str] = None telephone: str | None = None
user_id: Optional[str] = None user_id: int | None = None
user_name: Optional[str] = None user_name: str | None = None
status_code: Optional[int] = None status_code: int | None = None
client_ip: Optional[str] = None client_ip: str | None = None
request_method: Optional[str] = None request_method: str | None = None
api_path: Optional[str] = None api_path: str | None = None
system: Optional[str] = None system: str | None = None
browser: Optional[str] = None browser: str | None = None
summary: Optional[str] = None summary: str | None = None
route_name: Optional[str] = None route_name: str | None = None
description: Optional[str] = None description: str | None = None
tags: Optional[List[str]] = None tags: list[str] | None = None
process_time: Optional[str] = None process_time: float | None = None
params: Optional[str] = None params: str | None = None
class OperationRecordSimpleOut(OperationRecord): class OperationRecordSimpleOut(OperationRecord):
create_datetime: DatetimeStr model_config = ConfigDict(from_attributes=True)
class Config: create_datetime: DatetimeStr
orm_mode = True

View File

@ -7,18 +7,17 @@
# @desc : 简要说明 # @desc : 简要说明
from typing import Optional, List from pydantic import BaseModel, ConfigDict
from pydantic import BaseModel
from core.data_types import DatetimeStr from core.data_types import DatetimeStr
class SMSSendRecord(BaseModel): class SMSSendRecord(BaseModel):
telephone: str telephone: str
status: bool = True status: bool = True
user_id: Optional[int] = None user_id: int | None = None
content: Optional[str] = None content: str | None = None
desc: Optional[str] = None desc: str | None = None
scene: Optional[str] = None scene: str | None = None
class SMSSendRecordSimpleOut(SMSSendRecord): class SMSSendRecordSimpleOut(SMSSendRecord):
@ -26,5 +25,4 @@ class SMSSendRecordSimpleOut(SMSSendRecord):
create_datetime: DatetimeStr create_datetime: DatetimeStr
update_datetime: DatetimeStr update_datetime: DatetimeStr
class Config: model_config = ConfigDict(from_attributes=True)
orm_mode = True

View File

@ -12,11 +12,10 @@
import json import json
import os import os
from enum import Enum from enum import Enum
from typing import List, Union, Any from typing import Any
from aioredis import Redis from aioredis import Redis
from fastapi.encoders import jsonable_encoder from fastapi.encoders import jsonable_encoder
from motor.motor_asyncio import AsyncIOMotorDatabase from motor.motor_asyncio import AsyncIOMotorDatabase
from pymongo.results import InsertOneResult, UpdateResult
from sqlalchemy import select, update from sqlalchemy import select, update
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import joinedload from sqlalchemy.orm import joinedload
@ -34,7 +33,7 @@ class DictTypeDal(DalBase):
def __init__(self, db: AsyncSession): def __init__(self, db: AsyncSession):
super(DictTypeDal, self).__init__(db, models.VadminDictType, schemas.DictTypeSimpleOut) super(DictTypeDal, self).__init__(db, models.VadminDictType, schemas.DictTypeSimpleOut)
async def get_dicts_details(self, dict_types: List[str]) -> dict: async def get_dicts_details(self, dict_types: list[str]) -> dict:
""" """
获取多个字典类型下的字典元素列表 获取多个字典类型下的字典元素列表
""" """
@ -51,14 +50,14 @@ class DictTypeDal(DalBase):
data[obj.dict_type] = [] data[obj.dict_type] = []
continue continue
else: else:
data[obj.dict_type] = [schemas.DictDetailsSimpleOut.from_orm(i).dict() for i in obj.details] data[obj.dict_type] = [schemas.DictDetailsSimpleOut.model_validate(i).model_dump() for i in obj.details]
return data return data
async def get_select_datas(self): async def get_select_datas(self):
"""获取选择数据,全部数据""" """获取选择数据,全部数据"""
sql = select(self.model) sql = select(self.model)
queryset = await self.db.execute(sql) queryset = await self.db.execute(sql)
return [schemas.DictTypeSelectOut.from_orm(i).dict() for i in queryset.scalars().all()] return [schemas.DictTypeSelectOut.model_validate(i).model_dump() for i in queryset.scalars().all()]
class DictDetailsDal(DalBase): class DictDetailsDal(DalBase):
@ -128,7 +127,7 @@ class SettingsTabDal(DalBase):
def __init__(self, db: AsyncSession): def __init__(self, db: AsyncSession):
super(SettingsTabDal, self).__init__(db, models.VadminSystemSettingsTab, schemas.SettingsTabSimpleOut) super(SettingsTabDal, self).__init__(db, models.VadminSystemSettingsTab, schemas.SettingsTabSimpleOut)
async def get_classify_tab_values(self, classify: List[str], hidden: bool | None = False): async def get_classify_tab_values(self, classify: list[str], hidden: bool | None = False):
""" """
获取系统配置分类下的标签信息 获取系统配置分类下的标签信息
""" """
@ -144,7 +143,7 @@ class SettingsTabDal(DalBase):
) )
return self.__generate_values(datas) return self.__generate_values(datas)
async def get_tab_name_values(self, tab_names: List[str], hidden: bool | None = False): async def get_tab_name_values(self, tab_names: list[str], hidden: bool | None = False):
""" """
获取系统配置标签下的标签信息 获取系统配置标签下的标签信息
""" """
@ -161,7 +160,7 @@ class SettingsTabDal(DalBase):
return self.__generate_values(datas) return self.__generate_values(datas)
@classmethod @classmethod
def __generate_values(cls, datas: List[models.VadminSystemSettingsTab]): def __generate_values(cls, datas: list[models.VadminSystemSettingsTab]):
""" """
生成字典值 生成字典值
""" """
@ -398,7 +397,7 @@ class TaskDal(MongoManage):
""" """
创建任务 创建任务
""" """
data_dict = data.dict() data_dict = data.model_dump()
is_active = data_dict.pop('is_active') is_active = data_dict.pop('is_active')
insert_result = await super().create_data(data_dict) insert_result = await super().create_data(data_dict)
obj = await self.get_task(insert_result.inserted_id, v_schema=schemas.TaskSimpleOut) obj = await self.get_task(insert_result.inserted_id, v_schema=schemas.TaskSimpleOut)
@ -422,7 +421,7 @@ class TaskDal(MongoManage):
""" """
更新任务 更新任务
""" """
data_dict = data.dict() data_dict = data.model_dump()
is_active = data_dict.pop('is_active') is_active = data_dict.pop('is_active')
await super(TaskDal, self).put_data(_id, data) await super(TaskDal, self).put_data(_id, data)
obj: dict = await self.get_task(_id, v_schema=schemas.TaskSimpleOut) obj: dict = await self.get_task(_id, v_schema=schemas.TaskSimpleOut)

View File

@ -6,53 +6,48 @@
# @IDE : PyCharm # @IDE : PyCharm
# @desc : pydantic 模型,用于数据库序列化操作 # @desc : pydantic 模型,用于数据库序列化操作
# pydantic 验证数据https://blog.csdn.net/qq_44291044/article/details/104693526
from pydantic import BaseModel, ConfigDict
from typing import Optional, List
from pydantic import BaseModel
from core.data_types import DatetimeStr from core.data_types import DatetimeStr
class DictType(BaseModel): class DictType(BaseModel):
dict_name: str dict_name: str
dict_type: str dict_type: str
disabled: Optional[bool] = False disabled: bool | None = False
remark: Optional[str] = None remark: str | None = None
class DictTypeSimpleOut(DictType): class DictTypeSimpleOut(DictType):
model_config = ConfigDict(from_attributes=True)
id: int id: int
create_datetime: DatetimeStr create_datetime: DatetimeStr
update_datetime: DatetimeStr update_datetime: DatetimeStr
class Config:
orm_mode = True
class DictTypeSelectOut(BaseModel): class DictTypeSelectOut(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int id: int
dict_name: str dict_name: str
disabled: bool disabled: bool
class Config:
orm_mode = True
class DictDetails(BaseModel): class DictDetails(BaseModel):
label: str label: str
value: str value: str
disabled: Optional[bool] = False disabled: bool | None = False
is_default: Optional[bool] = False is_default: bool | None = False
remark: Optional[str] = None remark: str | None = None
order: Optional[int] = None order: int | None = None
dict_type_id: int dict_type_id: int
class DictDetailsSimpleOut(DictDetails): class DictDetailsSimpleOut(DictDetails):
model_config = ConfigDict(from_attributes=True)
id: int id: int
create_datetime: DatetimeStr create_datetime: DatetimeStr
update_datetime: DatetimeStr update_datetime: DatetimeStr
class Config:
orm_mode = True

View File

@ -6,27 +6,24 @@
# @IDE : PyCharm # @IDE : PyCharm
# @desc : pydantic 模型,用于数据库序列化操作 # @desc : pydantic 模型,用于数据库序列化操作
# pydantic 验证数据https://blog.csdn.net/qq_44291044/article/details/104693526
from pydantic import BaseModel, ConfigDict
from typing import Optional, List
from pydantic import BaseModel
from core.data_types import DatetimeStr from core.data_types import DatetimeStr
class Settings(BaseModel): class Settings(BaseModel):
config_label: Optional[str] = None config_label: str | None = None
config_key: str config_key: str
config_value: Optional[str] = None config_value: str | None = None
remark: Optional[str] = None remark: str | None = None
disabled: Optional[bool] = None disabled: bool | None = None
tab_id: int tab_id: int
class SettingsSimpleOut(Settings): class SettingsSimpleOut(Settings):
model_config = ConfigDict(from_attributes=True)
id: int id: int
create_datetime: DatetimeStr create_datetime: DatetimeStr
update_datetime: DatetimeStr update_datetime: DatetimeStr
class Config:
orm_mode = True

View File

@ -6,10 +6,8 @@
# @IDE : PyCharm # @IDE : PyCharm
# @desc : pydantic 模型,用于数据库序列化操作 # @desc : pydantic 模型,用于数据库序列化操作
# pydantic 验证数据https://blog.csdn.net/qq_44291044/article/details/104693526
from pydantic import BaseModel, ConfigDict
from pydantic import BaseModel
from core.data_types import DatetimeStr from core.data_types import DatetimeStr
@ -22,9 +20,9 @@ class SettingsTab(BaseModel):
class SettingsTabSimpleOut(SettingsTab): class SettingsTabSimpleOut(SettingsTab):
model_config = ConfigDict(from_attributes=True)
id: int id: int
create_datetime: DatetimeStr create_datetime: DatetimeStr
update_datetime: DatetimeStr update_datetime: DatetimeStr
class Config:
orm_mode = True

View File

@ -6,28 +6,27 @@
# @IDE : PyCharm # @IDE : PyCharm
# @desc : 简要说明 # @desc : 简要说明
from typing import Optional from pydantic import BaseModel, Field, ConfigDict
from pydantic import BaseModel, Field
from core.data_types import DatetimeStr, ObjectIdStr from core.data_types import DatetimeStr, ObjectIdStr
class Task(BaseModel): class Task(BaseModel):
name: str name: str
group: Optional[str] = None group: str | None = None
job_class: str job_class: str
exec_strategy: str exec_strategy: str
expression: str expression: str
is_active: Optional[bool] = True # 临时字段,不在表中创建 is_active: bool | None = True # 临时字段,不在表中创建
remark: Optional[str] = None remark: str | None = None
start_date: Optional[DatetimeStr] = None start_date: DatetimeStr | None = None
end_date: Optional[DatetimeStr] = None end_date: DatetimeStr | None = None
class TaskSimpleOut(Task): class TaskSimpleOut(Task):
model_config = ConfigDict(from_attributes=True)
id: ObjectIdStr = Field(..., alias='_id') id: ObjectIdStr = Field(..., alias='_id')
create_datetime: DatetimeStr create_datetime: DatetimeStr
update_datetime: DatetimeStr update_datetime: DatetimeStr
last_run_datetime: Optional[DatetimeStr] = None # 临时字段,不在表中创建 last_run_datetime: DatetimeStr | None = None # 临时字段,不在表中创建
class Config:
orm_mode = True

View File

@ -6,7 +6,6 @@
# @desc : 主要接口文件 # @desc : 主要接口文件
# UploadFile 库依赖pip install python-multipart # UploadFile 库依赖pip install python-multipart
from typing import List
from aioredis import Redis from aioredis import Redis
from fastapi import APIRouter, Depends, Body, UploadFile, Form from fastapi import APIRouter, Depends, Body, UploadFile, Form
from motor.motor_asyncio import AsyncIOMotorDatabase from motor.motor_asyncio import AsyncIOMotorDatabase
@ -17,7 +16,6 @@ from utils.file.aliyun_oss import AliyunOSS, BucketConf
from utils.file.file_manage import FileManage from utils.file.file_manage import FileManage
from utils.response import SuccessResponse, ErrorResponse from utils.response import SuccessResponse, ErrorResponse
from utils.sms.code import CodeSMS from utils.sms.code import CodeSMS
from utils.tools import generate_string
from . import schemas, crud from . import schemas, crud
from core.dependencies import IdList from core.dependencies import IdList
from apps.vadmin.auth.utils.current import AllUserAuth, FullAdminAuth, OpenAuth from apps.vadmin.auth.utils.current import AllUserAuth, FullAdminAuth, OpenAuth
@ -53,7 +51,7 @@ async def delete_dict_types(ids: IdList = Depends(), auth: Auth = Depends(AllUse
@app.post("/dict/types/details", summary="获取多个字典类型下的字典元素列表") @app.post("/dict/types/details", summary="获取多个字典类型下的字典元素列表")
async def post_dicts_details( async def post_dicts_details(
auth: Auth = Depends(AllUserAuth()), auth: Auth = Depends(AllUserAuth()),
dict_types: List[str] = Body(None, title="字典元素列表", description="查询字典元素列表") dict_types: list[str] = Body(None, title="字典元素列表", description="查询字典元素列表")
): ):
datas = await crud.DictTypeDal(auth.db).get_dicts_details(dict_types) datas = await crud.DictTypeDal(auth.db).get_dicts_details(dict_types)
return SuccessResponse(datas) return SuccessResponse(datas)

View File

@ -19,7 +19,7 @@
# https://www.osgeo.cn/sqlalchemy/orm/loading_relationships.html?highlight=selectinload#sqlalchemy.orm.joinedload # https://www.osgeo.cn/sqlalchemy/orm/loading_relationships.html?highlight=selectinload#sqlalchemy.orm.joinedload
import datetime import datetime
from typing import List, Set from typing import Set
from fastapi import HTTPException from fastapi import HTTPException
from fastapi.encoders import jsonable_encoder from fastapi.encoders import jsonable_encoder
from sqlalchemy import func, delete, update, or_ from sqlalchemy import func, delete, update, or_
@ -47,7 +47,7 @@ class DalBase:
data_id: int = None, data_id: int = None,
v_options: list = None, v_options: list = None,
v_join_query: dict = None, v_join_query: dict = None,
v_or: List[tuple] = None, v_or: list[tuple] = None,
v_order: str = None, v_order: str = None,
v_order_field: str = None, v_order_field: str = None,
v_return_none: bool = False, v_return_none: bool = False,
@ -82,7 +82,7 @@ class DalBase:
if not data and v_return_none: if not data and v_return_none:
return None return None
if data and v_schema: if data and v_schema:
return v_schema.from_orm(data).dict() return v_schema.model_validate(data).model_dump()
if data: if data:
return data return data
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="未找到此数据") raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="未找到此数据")
@ -93,7 +93,7 @@ class DalBase:
limit: int = 10, limit: int = 10,
v_options: list = None, v_options: list = None,
v_join_query: dict = None, v_join_query: dict = None,
v_or: List[tuple] = None, v_or: list[tuple] = None,
v_order: str = None, v_order: str = None,
v_order_field: str = None, v_order_field: str = None,
v_return_objs: bool = False, v_return_objs: bool = False,
@ -131,7 +131,7 @@ class DalBase:
return queryset.scalars().unique().all() return queryset.scalars().unique().all()
return [await self.out_dict(i, v_schema=v_schema) for i in queryset.scalars().unique().all()] return [await self.out_dict(i, v_schema=v_schema) for i in queryset.scalars().unique().all()]
async def get_count(self, v_options: list = None, v_join_query: dict = None, v_or: List[tuple] = None, **kwargs): async def get_count(self, v_options: list = None, v_join_query: dict = None, v_or: list[tuple] = None, **kwargs):
""" """
获取数据总数 获取数据总数
@ -156,7 +156,7 @@ class DalBase:
if isinstance(data, dict): if isinstance(data, dict):
obj = self.model(**data) obj = self.model(**data)
else: else:
obj = self.model(**data.dict()) obj = self.model(**data.model_dump())
await self.flush(obj) await self.flush(obj)
return await self.out_dict(obj, v_options, v_return_obj, v_schema) return await self.out_dict(obj, v_options, v_return_obj, v_schema)
@ -183,7 +183,7 @@ class DalBase:
await self.flush(obj) await self.flush(obj)
return await self.out_dict(obj, None, v_return_obj, v_schema) return await self.out_dict(obj, None, v_return_obj, v_schema)
async def delete_datas(self, ids: List[int], v_soft: bool = False, **kwargs): async def delete_datas(self, ids: list[int], v_soft: bool = False, **kwargs):
""" """
删除多条数据 删除多条数据
:param ids: 数据集 :param ids: 数据集
@ -200,13 +200,14 @@ class DalBase:
) )
else: else:
await self.db.execute(delete(self.model).where(self.model.id.in_(ids))) await self.db.execute(delete(self.model).where(self.model.id.in_(ids)))
await self.flush()
def add_filter_condition( def add_filter_condition(
self, self,
sql: select, sql: select,
v_options: list = None, v_options: list = None,
v_join_query: dict = None, v_join_query: dict = None,
v_or: List[tuple] = None, v_or: list[tuple] = None,
**kwargs **kwargs
) -> select: ) -> select:
""" """
@ -245,7 +246,7 @@ class DalBase:
sql = sql.options(*[load for load in v_options]) sql = sql.options(*[load for load in v_options])
return sql return sql
def __or_filter(self, sql: select, v_or: List[tuple], v_join_left: Set[str], v_join: Set[str]): def __or_filter(self, sql: select, v_or: list[tuple], v_join_left: Set[str], v_join: Set[str]):
""" """
或逻辑操作 或逻辑操作
:param sql: :param sql:
@ -337,5 +338,5 @@ class DalBase:
if v_return_obj: if v_return_obj:
return obj return obj
if v_schema: if v_schema:
return v_schema.from_orm(obj).dict() return v_schema.model_validate(obj).model_dump()
return self.schema.from_orm(obj).dict() return self.schema.model_validate(obj).model_dump()

View File

@ -1,84 +1,122 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# @version : 1.0 # @version : 1.0
# @Create Time : 2022/11/14 12:42 # @Create Time : 2023/7/16 12:42
# @File : data_types.py # @File : data_types.py
# @IDE : PyCharm # @IDE : PyCharm
# @desc : 自定义数据类型 # @desc : 自定义数据类型
""" """
自定义数据类型 - 官方文档https://pydantic-docs.helpmanual.io/usage/types/#custom-data-types 自定义数据类型 - 官方文档https://docs.pydantic.dev/dev-v2/usage/types/custom/#adding-validation-and-serialization
""" """
import datetime import datetime
from typing import Annotated, Any
from bson import ObjectId from bson import ObjectId
from pydantic import AfterValidator, PlainSerializer, WithJsonSchema
from .validator import * from .validator import *
class DatetimeStr(str): def DatetimeStrVali(value: str | datetime.datetime | int | float | dict):
"""
日期时间字符串验证
如果我传入的是字符串那么直接返回如果我传入的是一个日期类型那么会转为字符串格式后返回
因为在 pydantic 2.0 中是支持 int float 自动转换类型的所以我这里添加进去但是在处理时会使这两种类型报错
@classmethod 官方文档https://docs.pydantic.dev/dev-v2/usage/types/datetime/
def __get_validators__(cls): """
yield cls.validate if isinstance(value, str):
pattern = "%Y-%m-%d %H:%M:%S"
@classmethod try:
def validate(cls, v): datetime.datetime.strptime(value, pattern)
if isinstance(v, str): return value
return v except ValueError:
elif isinstance(v, dict): pass
# 转换为datetime对象 elif isinstance(value, datetime.datetime):
v = datetime.datetime.strptime(v.get("$date"), "%Y-%m-%dT%H:%M:%S.%fZ") return value.strftime("%Y-%m-%d %H:%M:%S")
return v.strftime("%Y-%m-%d %H:%M:%S") elif isinstance(value, dict):
# 用于处理 mongodb 日期时间数据类型
date_str = value.get("$date")
date_format = '%Y-%m-%dT%H:%M:%S.%fZ'
# 将字符串转换为datetime.datetime类型
datetime_obj = datetime.datetime.strptime(date_str, date_format)
# 将datetime.datetime对象转换为指定的字符串格式
return datetime_obj.strftime('%Y-%m-%d %H:%M:%S')
raise ValueError("无效的日期时间或字符串数据")
class Telephone(str): # 实现自定义一个日期时间字符串的数据类型
DatetimeStr = Annotated[
@classmethod str | datetime.datetime | int | float | dict,
def __get_validators__(cls): AfterValidator(DatetimeStrVali),
yield cls.validate PlainSerializer(lambda x: x, return_type=str),
WithJsonSchema({'type': 'string'}, mode='serialization')
@classmethod ]
def validate(cls, v):
return vali_telephone(v)
class Email(str): # 实现自定义一个手机号类型
Telephone = Annotated[
@classmethod str,
def __get_validators__(cls): AfterValidator(lambda x: vali_telephone(x)),
yield cls.validate PlainSerializer(lambda x: x, return_type=str),
WithJsonSchema({'type': 'string'}, mode='serialization')
@classmethod ]
def validate(cls, v):
return vali_email(v)
class DateStr(str): # 实现自定义一个邮箱类型
Email = Annotated[
@classmethod str,
def __get_validators__(cls): AfterValidator(lambda x: vali_email(x)),
yield cls.validate PlainSerializer(lambda x: x, return_type=str),
WithJsonSchema({'type': 'string'}, mode='serialization')
@classmethod ]
def validate(cls, v):
if isinstance(v, str):
return v
return v.strftime("%Y-%m-%d")
class ObjectIdStr(str): def DateStrVali(value: str | datetime.date | int | float):
"""
日期字符串验证
如果我传入的是字符串那么直接返回如果我传入的是一个日期类型那么会转为字符串格式后返回
因为在 pydantic 2.0 中是支持 int float 自动转换类型的所以我这里添加进去但是在处理时会使这两种类型报错
@classmethod 官方文档https://docs.pydantic.dev/dev-v2/usage/types/datetime/
def __get_validators__(cls): """
yield cls.validate if isinstance(value, str):
pattern = "%Y-%m-%d"
try:
datetime.datetime.strptime(value, pattern)
return value
except ValueError:
pass
elif isinstance(value, datetime.date):
return value.strftime("%Y-%m-%d")
raise ValueError("无效的日期时间或字符串数据")
@classmethod
def validate(cls, v): # 实现自定义一个日期字符串的数据类型
if isinstance(v, str): DateStr = Annotated[
return v str | datetime.date | int | float,
elif isinstance(v, dict): AfterValidator(DateStrVali),
return v.get("$oid") PlainSerializer(lambda x: x, return_type=str),
elif isinstance(v, ObjectId): WithJsonSchema({'type': 'string'}, mode='serialization')
return str(v) ]
return v
def ObjectIdStrVali(value: str | dict | ObjectId):
"""
官方文档https://docs.pydantic.dev/dev-v2/usage/types/datetime/
"""
if isinstance(value, str):
return value
elif isinstance(value, dict):
return value.get("$oid")
elif isinstance(value, ObjectId):
return str(value)
raise ValueError("无效的 ObjectId 数据类型")
ObjectIdStr = Annotated[
Any, # 这里不能直接使用 any需要使用 typing.Any
AfterValidator(ObjectIdStrVali),
PlainSerializer(lambda x: x, return_type=str),
WithJsonSchema({'type': 'string'}, mode='serialization')
]

View File

@ -10,7 +10,6 @@
类依赖项-官方文档https://fastapi.tiangolo.com/zh/tutorial/dependencies/classes-as-dependencies/ 类依赖项-官方文档https://fastapi.tiangolo.com/zh/tutorial/dependencies/classes-as-dependencies/
""" """
from typing import List
from fastapi import Body from fastapi import Body
import copy import copy
@ -24,7 +23,7 @@ class QueryParams:
self.v_order = params.v_order self.v_order = params.v_order
self.v_order_field = params.v_order_field self.v_order_field = params.v_order_field
def dict(self, exclude: List[str] = None) -> dict: def dict(self, exclude: list[str] = None) -> dict:
result = copy.deepcopy(self.__dict__) result = copy.deepcopy(self.__dict__)
if exclude: if exclude:
for item in exclude: for item in exclude:
@ -34,7 +33,7 @@ class QueryParams:
pass pass
return result return result
def to_count(self, exclude: List[str] = None) -> dict: def to_count(self, exclude: list[str] = None) -> dict:
params = self.dict(exclude=exclude) params = self.dict(exclude=exclude)
del params["page"] del params["page"]
del params["limit"] del params["limit"]
@ -59,5 +58,5 @@ class IdList:
""" """
id 列表 id 列表
""" """
def __init__(self, ids: List[int] = Body(..., title="ID 列表")): def __init__(self, ids: list[int] = Body(..., title="ID 列表")):
self.ids = ids self.ids = ids

View File

@ -16,10 +16,11 @@ alibabacloud-tea-util==0.3.8
alibabacloud-tea-xml==0.0.2 alibabacloud-tea-xml==0.0.2
aliyun-python-sdk-core==2.13.36 aliyun-python-sdk-core==2.13.36
aliyun-python-sdk-kms==2.16.0 aliyun-python-sdk-kms==2.16.0
annotated-types==0.5.0
anyio==3.6.2 anyio==3.6.2
asgiref==3.5.2 asgiref==3.5.2
async-timeout==4.0.2 async-timeout==4.0.2
asyncmy==0.2.5 asyncmy==0.2.8
attrs==22.1.0 attrs==22.1.0
bcrypt==4.0.1 bcrypt==4.0.1
certifi==2022.9.24 certifi==2022.9.24
@ -32,7 +33,7 @@ cryptography==38.0.3
dnspython==2.2.1 dnspython==2.2.1
ecdsa==0.18.0 ecdsa==0.18.0
et-xmlfile==1.1.0 et-xmlfile==1.1.0
fastapi==0.97.0 fastapi==0.100.0
frozenlist==1.3.3 frozenlist==1.3.3
greenlet==2.0.1 greenlet==2.0.1
gunicorn==20.1.0 gunicorn==20.1.0
@ -47,6 +48,7 @@ Mako==1.2.4
MarkupSafe==2.1.1 MarkupSafe==2.1.1
motor==3.1.1 motor==3.1.1
multidict==6.0.2 multidict==6.0.2
openai==0.27.4
openpyxl==3.0.10 openpyxl==3.0.10
orjson==3.8.2 orjson==3.8.2
oss2==2.16.0 oss2==2.16.0
@ -55,7 +57,8 @@ Pillow==9.3.0
pyasn1==0.4.8 pyasn1==0.4.8
pycparser==2.21 pycparser==2.21
pycryptodome==3.15.0 pycryptodome==3.15.0
pydantic==1.10.2 pydantic==2.0.3
pydantic-core==2.3.0
PyJWT==2.6.0 PyJWT==2.6.0
pymongo==4.3.3 pymongo==4.3.3
PyMySQL==1.0.2 PyMySQL==1.0.2
@ -70,8 +73,9 @@ SQLAlchemy==1.4.44
SQLAlchemy-Utils==0.38.3 SQLAlchemy-Utils==0.38.3
SSIM-PIL==1.0.14 SSIM-PIL==1.0.14
starlette==0.27.0 starlette==0.27.0
tqdm==4.65.0
typer==0.7.0 typer==0.7.0
typing-extensions==4.4.0 typing-extensions==4.7.1
ua-parser==0.16.1 ua-parser==0.16.1
urllib3==1.26.12 urllib3==1.26.12
user-agents==2.2.0 user-agents==2.2.0

View File

@ -14,7 +14,6 @@ import datetime
import hashlib import hashlib
import os.path import os.path
import random import random
from typing import List
import xlsxwriter import xlsxwriter
from application.settings import TEMP_DIR, TEMP_URL from application.settings import TEMP_DIR, TEMP_URL
@ -45,7 +44,7 @@ class WriteXlsx:
self.wb = xlsxwriter.Workbook(self.filename) self.wb = xlsxwriter.Workbook(self.filename)
self.sheet = self.wb.add_worksheet(self.sheet_name) self.sheet = self.wb.add_worksheet(self.sheet_name)
def generate_template(self, headers: List[dict] = None, max_row: int = 101) -> None: def generate_template(self, headers: list[dict] = None, max_row: int = 101) -> None:
""" """
生成模板 生成模板
:param headers: 表头 :param headers: 表头

View File

@ -16,24 +16,22 @@ https://api.ip138.com/ip/?ip=58.16.180.3&datatype=jsonp&token=cc87f3c77747bccbaa
aiohttp 异步请求文档https://docs.aiohttp.org/en/stable/client_quickstart.html aiohttp 异步请求文档https://docs.aiohttp.org/en/stable/client_quickstart.html
""" """
from aiohttp import TCPConnector from aiohttp import TCPConnector
from application.settings import IP_PARSE_TOKEN, IP_PARSE_ENABLE from application.settings import IP_PARSE_TOKEN, IP_PARSE_ENABLE
import aiohttp import aiohttp
from core.logger import logger from core.logger import logger
from pydantic import BaseModel from pydantic import BaseModel
from typing import Optional
class IPLocationOut(BaseModel): class IPLocationOut(BaseModel):
ip: Optional[str] = None ip: str | None = None
address: Optional[str] = None address: str | None = None
country: Optional[str] = None country: str | None = None
province: Optional[str] = None province: str | None = None
city: Optional[str] = None city: str | None = None
county: Optional[str] = None county: str | None = None
operator: Optional[str] = None operator: str | None = None
postal_code: Optional[str] = None postal_code: str | None = None
area_code: Optional[str] = None area_code: str | None = None
class IPManage: class IPManage: