fix pydantic

This commit is contained in:
jxxghp
2025-11-01 09:51:23 +08:00
parent 09a19e94d5
commit d523c7c916
47 changed files with 160 additions and 135 deletions

View File

@@ -235,7 +235,7 @@ class ConversationMemoryManager:
# 保存到Redis设置TTL自动过期
if settings.CACHE_BACKEND_TYPE == "redis":
try:
memory_dict = memory.dict()
memory_dict = memory.model_dump()
redis_key = f"agent_memory:{memory.user_id}:{memory.session_id}" if memory.user_id else f"agent_memory:{memory.session_id}"
ttl = int(timedelta(days=settings.LLM_REDIS_MEMORY_RETENTION_DAYS).total_seconds())
await self.redis_helper.set(

View File

@@ -21,7 +21,7 @@ class PromptManager:
if prompt_name in self.prompts_cache:
return self.prompts_cache[prompt_name]
prompt_file = self.prompts_dir / "prompt" / prompt_name
prompt_file = self.prompts_dir / prompt_name
try:
with open(prompt_file, 'r', encoding='utf-8') as f:

View File

@@ -27,7 +27,7 @@ class QueryDownloadsTool(MoviePilotTool):
continue
filtered_downloads.append(dl)
if filtered_downloads:
return json.dumps([d.dict() for d in filtered_downloads])
return json.dumps([d.model_dump() for d in filtered_downloads])
return "未找到相关下载任务。"
except Exception as e:
logger.error(f"查询下载失败: {e}", exc_info=True)

View File

@@ -3,7 +3,7 @@
import json
from typing import Optional
from app.db.media_oper import MediaOper
from app.db.mediaserver_oper import MediaServerOper
from app.log import logger
from app.agent.tools.base import MoviePilotTool
@@ -16,8 +16,8 @@ class QueryMediaLibraryTool(MoviePilotTool):
title: Optional[str] = None) -> str:
logger.info(f"执行工具: {self.name}, 参数: media_type={media_type}, title={title}")
try:
media_oper = MediaOper()
medias = media_oper.list()
media_server_oper = MediaServerOper()
medias = media_server_oper.list()
filtered_medias = []
for media in medias:
if media_type != "all" and media.type != media_type:

View File

@@ -137,7 +137,7 @@ async def transfer(days: Optional[int] = 7,
return [stat[1] for stat in transfer_stat]
@router.get("/cpu", summary="获取当前CPU使用率", response_model=int)
@router.get("/cpu", summary="获取当前CPU使用率", response_model=float)
def cpu(_: schemas.TokenPayload = Depends(verify_token)) -> Any:
"""
获取当前CPU使用率
@@ -145,7 +145,7 @@ def cpu(_: schemas.TokenPayload = Depends(verify_token)) -> Any:
return SystemUtils.cpu_usage()
@router.get("/cpu2", summary="获取当前CPU使用率API_TOKEN", response_model=int)
@router.get("/cpu2", summary="获取当前CPU使用率API_TOKEN", response_model=float)
def cpu2(_: Annotated[str, Depends(verify_apitoken)]) -> Any:
"""
获取当前CPU使用率 API_TOKEN认证?token=xxx

View File

@@ -40,10 +40,10 @@ def download(
metainfo = MetaInfo(title=torrent_in.title, subtitle=torrent_in.description)
# 媒体信息
mediainfo = MediaInfo()
mediainfo.from_dict(media_in.dict())
mediainfo.from_dict(media_in.model_dump())
# 种子信息
torrentinfo = TorrentInfo()
torrentinfo.from_dict(torrent_in.dict())
torrentinfo.from_dict(torrent_in.model_dump())
# 手动下载始终使用选择的下载器
torrentinfo.site_downloader = downloader
# 上下文
@@ -81,7 +81,7 @@ def add(
return schemas.Response(success=False, message="无法识别媒体信息")
# 种子信息
torrentinfo = TorrentInfo()
torrentinfo.from_dict(torrent_in.dict())
torrentinfo.from_dict(torrent_in.model_dump())
# 上下文
context = Context(
meta_info=metainfo,

View File

@@ -79,7 +79,7 @@ def exists(media_in: schemas.MediaInfo,
"""
# 转化为媒体信息对象
mediainfo = MediaInfo()
mediainfo.from_dict(media_in.dict())
mediainfo.from_dict(media_in.model_dump())
existsinfo: schemas.ExistMediaInfo = MediaServerChain().media_exists(mediainfo=mediainfo)
if not existsinfo:
return []
@@ -108,7 +108,7 @@ def not_exists(media_in: schemas.MediaInfo,
meta.year = media_in.year
# 转化为媒体信息对象
mediainfo = MediaInfo()
mediainfo.from_dict(media_in.dict())
mediainfo.from_dict(media_in.model_dump())
exist_flag, no_exists = DownloadChain().get_no_exists_info(meta=meta, mediainfo=mediainfo)
mediakey = mediainfo.tmdb_id or mediainfo.douban_id
if mediainfo.type == MediaType.MOVIE:

View File

@@ -132,7 +132,7 @@ async def subscribe(subscription: schemas.Subscription, _: schemas.TokenPayload
"""
客户端webpush通知订阅
"""
subinfo = subscription.dict()
subinfo = subscription.model_dump()
if subinfo not in global_vars.get_subscriptions():
global_vars.push_subscription(subinfo)
logger.debug(f"通知订阅成功: {subinfo}")
@@ -148,7 +148,7 @@ def send_notification(payload: schemas.SubscriptionMessage, _: schemas.TokenPayl
try:
webpush(
subscription_info=sub,
data=json.dumps(payload.dict()),
data=json.dumps(payload.model_dump()),
vapid_private_key=settings.VAPID.get("privateKey"),
vapid_claims={
"sub": settings.VAPID.get("subject")

View File

@@ -67,7 +67,7 @@ async def add_site(
site_in.name = site_info.get("name")
site_in.id = None
site_in.public = 1 if site_info.get("public") else 0
site = Site(**site_in.dict())
site = Site(**site_in.model_dump())
site.create(db)
# 通知站点更新
await eventmanager.async_send_event(EventType.SiteUpdated, {
@@ -92,7 +92,7 @@ async def update_site(
# 校正地址格式
_scheme, _netloc = StringUtils.get_url_netloc(site_in.url)
site_in.url = f"{_scheme}://{_netloc}/"
await site.async_update(db, site_in.dict())
await site.async_update(db, site_in.model_dump())
# 通知站点更新
await eventmanager.async_send_event(EventType.SiteUpdated, {
"domain": site_in.domain
@@ -399,7 +399,7 @@ def auth_site(
if not auth_info or not auth_info.site or not auth_info.params:
return schemas.Response(success=False, message="请输入认证站点和认证参数")
status, msg = SitesHelper().check_user(auth_info.site, auth_info.params)
SystemConfigOper().set(SystemConfigKey.UserSiteAuthParams, auth_info.dict())
SystemConfigOper().set(SystemConfigKey.UserSiteAuthParams, auth_info.model_dump())
# 认证成功后,重新初始化插件
PluginManager().init_config()
Scheduler().init_plugin_jobs()

View File

@@ -79,7 +79,7 @@ async def create_subscribe(
# 订阅用户
subscribe_in.username = current_user.name
# 转化为字典
subscribe_dict = subscribe_in.dict()
subscribe_dict = subscribe_in.model_dump()
if subscribe_in.id:
subscribe_dict.pop("id", None)
sid, message = await SubscribeChain().async_add(mtype=mtype,
@@ -106,7 +106,7 @@ async def update_subscribe(
return schemas.Response(success=False, message="订阅不存在")
# 避免更新缺失集数
old_subscribe_dict = subscribe.to_dict()
subscribe_dict = subscribe_in.dict()
subscribe_dict = subscribe_in.model_dump()
if not subscribe_in.lack_episode:
# 没有缺失集数时缺失集数清空避免更新为0
subscribe_dict.pop("lack_episode")
@@ -529,7 +529,7 @@ async def subscribe_fork(
"""
复用订阅
"""
sub_dict = sub.dict()
sub_dict = sub.model_dump()
sub_dict.pop("id")
for key in list(sub_dict.keys()):
if not hasattr(schemas.Subscribe(), key):

View File

@@ -41,7 +41,7 @@ async def create_user(
user = await current_user.async_get_by_name(db, name=user_in.name)
if user:
return schemas.Response(success=False, message="用户已存在")
user_info = user_in.dict()
user_info = user_in.model_dump()
if user_info.get("password"):
user_info["hashed_password"] = get_password_hash(user_info["password"])
user_info.pop("password")
@@ -59,7 +59,7 @@ async def update_user(
"""
更新用户
"""
user_info = user_in.dict()
user_info = user_in.model_dump()
if user_info.get("password"):
# 正则表达式匹配密码包含字母、数字、特殊字符中的至少两项
pattern = r'^(?![a-zA-Z]+$)(?!\d+$)(?![^\da-zA-Z\s]+$).{6,50}$'

View File

@@ -47,7 +47,7 @@ async def create_workflow(workflow: schemas.Workflow,
workflow.state = "P"
if not workflow.trigger_type:
workflow.trigger_type = "timer"
workflow_obj = Workflow(**workflow.dict())
workflow_obj = Workflow(**workflow.model_dump())
await workflow_obj.async_create(db)
return schemas.Response(success=True, message="创建工作流成功")
@@ -277,7 +277,7 @@ def update_workflow(workflow: schemas.Workflow,
return schemas.Response(success=False, message="工作流不存在")
if not wf.trigger_type:
workflow.trigger_type = "timer"
wf.update(db, workflow.dict())
wf.update(db, workflow.model_dump())
# 更新后的工作流对象
updated_workflow = workflow_oper.get(workflow.id)
# 更新定时任务

View File

@@ -854,7 +854,7 @@ class ChainBase(metaclass=ABCMeta):
torrentinfo=torrentinfo, transferinfo=transferinfo, **kwargs)
# 保存消息
self.messagehelper.put(message, role="user", title=message.title)
self.messageoper.add(**message.dict())
self.messageoper.add(**message.model_dump())
# 发送消息按设置隔离
if not message.userid and message.mtype:
# 消息隔离设置
@@ -901,12 +901,12 @@ class ChainBase(metaclass=ABCMeta):
break
# 按设定发送
self.eventmanager.send_event(etype=EventType.NoticeMessage,
data={**send_message.dict(), "type": send_message.mtype})
data={**send_message.model_dump(), "type": send_message.mtype})
self.messagequeue.send_message("post_message", message=send_message)
if not send_orignal:
return
# 发送消息事件
self.eventmanager.send_event(etype=EventType.NoticeMessage, data={**message.dict(), "type": message.mtype})
self.eventmanager.send_event(etype=EventType.NoticeMessage, data={**message.model_dump(), "type": message.mtype})
# 按原消息发送
self.messagequeue.send_message("post_message", message=message,
immediately=True if message.userid else False)
@@ -933,7 +933,7 @@ class ChainBase(metaclass=ABCMeta):
torrentinfo=torrentinfo, transferinfo=transferinfo, **kwargs)
# 保存消息
self.messagehelper.put(message, role="user", title=message.title)
await self.messageoper.async_add(**message.dict())
await self.messageoper.async_add(**message.model_dump())
# 发送消息按设置隔离
if not message.userid and message.mtype:
# 消息隔离设置
@@ -980,13 +980,13 @@ class ChainBase(metaclass=ABCMeta):
break
# 按设定发送
await self.eventmanager.async_send_event(etype=EventType.NoticeMessage,
data={**send_message.dict(), "type": send_message.mtype})
data={**send_message.model_dump(), "type": send_message.mtype})
await self.messagequeue.async_send_message("post_message", message=send_message)
if not send_orignal:
return
# 发送消息事件
await self.eventmanager.async_send_event(etype=EventType.NoticeMessage,
data={**message.dict(), "type": message.mtype})
data={**message.model_dump(), "type": message.mtype})
# 按原消息发送
await self.messagequeue.async_send_message("post_message", message=message,
immediately=True if message.userid else False)
@@ -1000,7 +1000,7 @@ class ChainBase(metaclass=ABCMeta):
"""
note_list = [media.to_dict() for media in medias]
self.messagehelper.put(message, role="user", note=note_list, title=message.title)
self.messageoper.add(**message.dict(), note=note_list)
self.messageoper.add(**message.model_dump(), note=note_list)
return self.messagequeue.send_message("post_medias_message", message=message, medias=medias,
immediately=True if message.userid else False)
@@ -1013,7 +1013,7 @@ class ChainBase(metaclass=ABCMeta):
"""
note_list = [torrent.torrent_info.to_dict() for torrent in torrents]
self.messagehelper.put(message, role="user", note=note_list, title=message.title)
self.messageoper.add(**message.dict(), note=note_list)
self.messageoper.add(**message.model_dump(), note=note_list)
return self.messagequeue.send_message("post_torrents_message", message=message, torrents=torrents,
immediately=True if message.userid else False)

View File

@@ -994,7 +994,7 @@ class DownloadChain(ChainBase):
# 发出下载任务删除事件,如需处理辅种,可监听该事件
self.eventmanager.send_event(EventType.DownloadDeleted, {
"hash": hash_str,
"torrents": [torrent.dict() for torrent in torrents]
"torrents": [torrent.model_dump() for torrent in torrents]
})
else:
logger.info(f"没有在下载器中查询到 {hash_str} 对应的下载任务")

View File

@@ -167,7 +167,7 @@ class MediaServerChain(ChainBase):
for episode in espisodes_info:
seasoninfo[episode.season] = episode.episodes
# 插入数据
item_dict = item.dict()
item_dict = item.model_dump()
item_dict["seasoninfo"] = seasoninfo
item_dict["item_type"] = item_type
dboper.add(**item_dict)

View File

@@ -56,7 +56,7 @@ class SiteChain(ChainBase):
if userdata:
SiteOper().update_userdata(domain=StringUtils.get_url_domain(site.get("domain")),
name=site.get("name"),
payload=userdata.dict())
payload=userdata.model_dump())
# 发送事件
eventmanager.send_event(EventType.SiteRefreshed, {
"site_id": site.get("id")

View File

@@ -180,7 +180,7 @@ class WorkflowExecutor:
"""
合并上下文
"""
for key, value in context.dict().items():
for key, value in context.model_dump().items():
if not getattr(self.context, key, None):
setattr(self.context, key, value)

View File

@@ -11,7 +11,8 @@ from typing import Any, Dict, List, Optional, Tuple, Type
from urllib.parse import urlparse
from dotenv import set_key
from pydantic import BaseModel, BaseSettings, validator, Field
from pydantic import BaseModel, Field, ConfigDict, model_validator
from pydantic_settings import BaseSettings
from app.log import logger, log_settings, LogConfigModel
from app.schemas import MediaType
@@ -49,8 +50,7 @@ class ConfigModel(BaseModel):
Pydantic 配置模型,描述所有配置项及其类型和默认值
"""
class Config:
extra = "ignore" # 忽略未定义的配置项
model_config = ConfigDict(extra="ignore") # 忽略未定义的配置项
# ==================== 基础应用配置 ====================
# 项目名称
@@ -92,7 +92,7 @@ class ConfigModel(BaseModel):
# 超级管理员初始用户名
SUPERUSER: str = "admin"
# 超级管理员初始密码
SUPERUSER_PASSWORD: str = None
SUPERUSER_PASSWORD: Optional[str] = None
# 辅助认证,允许通过外部服务进行认证、单点登录以及自动创建用户
AUXILIARY_AUTH_ENABLE: bool = False
# API密钥需要更换
@@ -398,9 +398,9 @@ class ConfigModel(BaseModel):
# ==================== 存储配置 ====================
# 对rclone进行快照对比时是否检查文件夹的修改时间
RCLONE_SNAPSHOT_CHECK_FOLDER_MODTIME = True
RCLONE_SNAPSHOT_CHECK_FOLDER_MODTIME: bool = True
# 对OpenList进行快照对比时是否检查文件夹的修改时间
OPENLIST_SNAPSHOT_CHECK_FOLDER_MODTIME = True
OPENLIST_SNAPSHOT_CHECK_FOLDER_MODTIME: bool = True
# ==================== Docker配置 ====================
# Docker Client API地址
@@ -438,10 +438,11 @@ class Settings(BaseSettings, ConfigModel, LogConfigModel):
系统配置类
"""
class Config:
case_sensitive = True
env_file = SystemUtils.get_env_path()
env_file_encoding = "utf-8"
model_config = ConfigDict(
case_sensitive=True,
env_file=SystemUtils.get_env_path(),
env_file_encoding="utf-8"
)
def __init__(self, **kwargs):
super().__init__(**kwargs)
@@ -538,19 +539,48 @@ class Settings(BaseSettings, ConfigModel, LogConfigModel):
f"配置项 '{field_name}' 的值 '{value}' 无法转换成正确的类型,使用默认值 '{default}',错误信息: {e}")
return default, True
@validator('*', pre=True, always=True)
def generic_type_validator(cls, value: Any, field): # noqa
@model_validator(mode='before')
@classmethod
def generic_type_validator(cls, data: Any): # noqa
"""
通用校验器,尝试将配置值转换为期望的类型
"""
if field.name == "API_TOKEN":
converted_value, needs_update = cls.validate_api_token(value, value)
else:
converted_value, needs_update = cls.generic_type_converter(value, value, field.type_, field.default,
field.name)
if needs_update:
cls.update_env_config(field, value, converted_value)
return converted_value
if not isinstance(data, dict):
return data
# 处理 API_TOKEN 特殊验证
if 'API_TOKEN' in data:
converted_value, needs_update = cls.validate_api_token(data['API_TOKEN'], data['API_TOKEN'])
if needs_update:
cls.update_env_config(
type('Field', (), {'name': 'API_TOKEN'})(),
data['API_TOKEN'],
converted_value
)
data['API_TOKEN'] = converted_value
# 对其他字段进行类型转换
for field_name, field_info in cls.model_fields.items():
if field_name not in data:
continue
value = data[field_name]
if value is None:
continue
field = cls.model_fields.get(field_name)
if field:
converted_value, needs_update = cls.generic_type_converter(
value, value, field.annotation, field.default, field_name
)
if needs_update:
cls.update_env_config(
type('Field', (), {'name': field_name})(),
value,
converted_value
)
data[field_name] = converted_value
return data
@staticmethod
def update_env_config(field: Any, original_value: Any, converted_value: Any) -> Tuple[bool, str]:

View File

@@ -128,10 +128,10 @@ class TransferHistoryOper(DbOper):
self.add_force(
src=fileitem.path,
src_storage=fileitem.storage,
src_fileitem=fileitem.dict(),
src_fileitem=fileitem.model_dump(),
dest=transferinfo.target_item.path if transferinfo.target_item else None,
dest_storage=transferinfo.target_item.storage if transferinfo.target_item else None,
dest_fileitem=transferinfo.target_item.dict() if transferinfo.target_item else None,
dest_fileitem=transferinfo.target_item.model_dump() if transferinfo.target_item else None,
mode=mode,
type=mediainfo.type.value,
category=mediainfo.category,
@@ -159,10 +159,10 @@ class TransferHistoryOper(DbOper):
his = self.add_force(
src=fileitem.path,
src_storage=fileitem.storage,
src_fileitem=fileitem.dict(),
src_fileitem=fileitem.model_dump(),
dest=transferinfo.target_item.path if transferinfo.target_item else None,
dest_storage=transferinfo.target_item.storage if transferinfo.target_item else None,
dest_fileitem=transferinfo.target_item.dict() if transferinfo.target_item else None,
dest_fileitem=transferinfo.target_item.model_dump() if transferinfo.target_item else None,
mode=mode,
type=mediainfo.type.value,
category=mediainfo.category,
@@ -188,7 +188,7 @@ class TransferHistoryOper(DbOper):
year=meta.year,
src=fileitem.path,
src_storage=fileitem.storage,
src_fileitem=fileitem.dict(),
src_fileitem=fileitem.model_dump(),
mode=mode,
seasons=meta.season,
episodes=meta.episode,

View File

@@ -47,7 +47,7 @@ class StorageHelper:
if s.type == storage:
s.config = conf
break
SystemConfigOper().set(SystemConfigKey.Storages, [s.dict() for s in storagies])
SystemConfigOper().set(SystemConfigKey.Storages, [s.model_dump() for s in storagies])
def add_storage(self, storage: str, name: str, conf: dict):
"""
@@ -68,7 +68,7 @@ class StorageHelper:
name=name,
config=conf
))
SystemConfigOper().set(SystemConfigKey.Storages, [s.dict() for s in storagies])
SystemConfigOper().set(SystemConfigKey.Storages, [s.model_dump() for s in storagies])
def reset_storage(self, storage: str):
"""
@@ -79,4 +79,4 @@ class StorageHelper:
if s.type == storage:
s.config = {}
break
SystemConfigOper().set(SystemConfigKey.Storages, [s.dict() for s in storagies])
SystemConfigOper().set(SystemConfigKey.Storages, [s.model_dump() for s in storagies])

View File

@@ -11,7 +11,8 @@ from pathlib import Path
from typing import Dict, Any, Optional
import click
from pydantic import BaseSettings, BaseModel
from pydantic import BaseModel, ConfigDict
from pydantic_settings import BaseSettings
from app.utils.system import SystemUtils
@@ -21,8 +22,7 @@ class LogConfigModel(BaseModel):
Pydantic 配置模型,描述所有配置项及其类型和默认值
"""
class Config:
extra = "ignore" # 忽略未定义的配置项
model_config = ConfigDict(extra="ignore") # 忽略未定义的配置项
# 配置文件目录
CONFIG_DIR: Optional[str] = None
@@ -71,10 +71,11 @@ class LogSettings(BaseSettings, LogConfigModel):
"""
return self.LOG_MAX_FILE_SIZE * 1024 * 1024
class Config:
case_sensitive = True
env_file = SystemUtils.get_env_path()
env_file_encoding = "utf-8"
model_config = ConfigDict(
case_sensitive=True,
env_file=SystemUtils.get_env_path(),
env_file_encoding="utf-8"
)
# 实例化日志设置

View File

@@ -154,7 +154,7 @@ class FilterModule(_ModuleBase):
custom_rules = self.rulehelper.get_custom_rules()
for rule in custom_rules:
logger.info(f"加载自定义规则 {rule.id} - {rule.name}")
self.rule_set[rule.id] = rule.dict()
self.rule_set[rule.id] = rule.model_dump()
@staticmethod
def get_name() -> str:

View File

@@ -2,7 +2,7 @@
from datetime import datetime
from typing import Dict, List, Optional, Any
from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, ConfigDict, field_serializer
class ConversationMemory(BaseModel):
@@ -16,10 +16,11 @@ class ConversationMemory(BaseModel):
created_at: datetime = Field(default_factory=datetime.now, description="创建时间")
updated_at: datetime = Field(default_factory=datetime.now, description="更新时间")
class Config:
json_encoders = {
datetime: lambda v: v.isoformat()
}
model_config = ConfigDict()
@field_serializer('created_at', 'updated_at', when_used='json')
def serialize_datetime(self, value: datetime) -> str:
return value.isoformat()
class AgentState(BaseModel):
@@ -30,10 +31,11 @@ class AgentState(BaseModel):
is_thinking: bool = Field(default=False, description="是否正在思考")
last_activity: datetime = Field(default_factory=datetime.now, description="最后活动时间")
class Config:
json_encoders = {
datetime: lambda v: v.isoformat()
}
model_config = ConfigDict()
@field_serializer('last_activity', when_used='json')
def serialize_datetime(self, value: datetime) -> str:
return value.isoformat()
class UserMessage(BaseModel):

View File

@@ -1,7 +1,7 @@
from pathlib import Path
from typing import Optional, Dict, Any, List, Set, Callable
from pydantic import BaseModel, Field, root_validator
from pydantic import BaseModel, Field, model_validator
from app.schemas.message import MessageChannel
from app.schemas.file import FileItem
@@ -68,7 +68,8 @@ class AuthCredentials(ChainEventData):
channel: Optional[str] = Field(default=None, description="认证渠道")
service: Optional[str] = Field(default=None, description="服务名称")
@root_validator(pre=True)
@model_validator(mode='before')
@classmethod
def check_fields_based_on_grant_type(cls, values): # noqa
grant_type = values.get("grant_type")
if not grant_type:

View File

@@ -1,6 +1,6 @@
from typing import Optional, Any
from pydantic import BaseModel
from pydantic import BaseModel, ConfigDict
class DownloadHistory(BaseModel):
@@ -51,8 +51,7 @@ class DownloadHistory(BaseModel):
# 自定义剧集组
episode_group: Optional[str] = None
class Config:
orm_mode = True
model_config = ConfigDict(from_attributes=True)
class TransferHistory(BaseModel):
@@ -97,5 +96,4 @@ class TransferHistory(BaseModel):
# 日期
date: Optional[str] = None
class Config:
orm_mode = True
model_config = ConfigDict(from_attributes=True)

View File

@@ -1,7 +1,7 @@
from pathlib import Path
from typing import Optional, Dict, Union, List, Any
from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, ConfigDict
from app.schemas.types import MediaType
@@ -125,8 +125,7 @@ class MediaServerItem(BaseModel):
lst_mod_date: Optional[str] = None
user_state: Optional[MediaServerItemUserState] = None
class Config:
orm_mode = True
model_config = ConfigDict(from_attributes=True)
class MediaServerSeasonInfo(BaseModel):

View File

@@ -40,7 +40,7 @@ class CommingMessage(BaseModel):
"""
转换为字典
"""
items = self.dict()
items = self.model_dump()
for k, v in items.items():
if isinstance(v, MessageChannel):
items[k] = v.value
@@ -88,7 +88,7 @@ class Notification(BaseModel):
"""
转换为字典
"""
items = self.dict()
items = self.model_dump()
for k, v in items.items():
if isinstance(v, MessageChannel) \
or isinstance(v, NotificationType):

View File

@@ -1,6 +1,6 @@
from typing import Optional, Any, Union, Dict
from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, ConfigDict
class Site(BaseModel):
@@ -47,8 +47,7 @@ class Site(BaseModel):
# 下载器
downloader: Optional[str] = None
class Config:
orm_mode = True
model_config = ConfigDict(from_attributes=True)
class SiteStatistic(BaseModel):
@@ -67,8 +66,7 @@ class SiteStatistic(BaseModel):
# 备注
note: Optional[Any] = None
class Config:
orm_mode = True
model_config = ConfigDict(from_attributes=True)
class SiteUserData(BaseModel):

View File

@@ -1,6 +1,6 @@
from typing import Optional, List, Dict, Any
from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, ConfigDict
class Subscribe(BaseModel):
@@ -76,8 +76,7 @@ class Subscribe(BaseModel):
# 剧集组
episode_group: Optional[str] = None
class Config:
orm_mode = True
model_config = ConfigDict(from_attributes=True)
class SubscribeShare(BaseModel):

View File

@@ -73,10 +73,10 @@ class TransferTask(BaseModel):
返回字典
"""
dicts = vars(self).copy()
dicts["fileitem"] = self.fileitem.dict() if self.fileitem else None
dicts["meta"] = self.meta.dict() if self.meta else None
dicts["mediainfo"] = self.mediainfo.dict() if self.mediainfo else None
dicts["target_directory"] = self.target_directory.dict() if self.target_directory else None
dicts["fileitem"] = self.fileitem.model_dump() if self.fileitem else None
dicts["meta"] = self.meta.model_dump() if self.meta else None
dicts["mediainfo"] = self.mediainfo.model_dump() if self.mediainfo else None
dicts["target_directory"] = self.target_directory.model_dump() if self.target_directory else None
return dicts
@@ -144,8 +144,8 @@ class TransferInfo(BaseModel):
返回字典
"""
dicts = vars(self).copy()
dicts["fileitem"] = self.fileitem.dict() if self.fileitem else None
dicts["target_item"] = self.target_item.dict() if self.target_item else None
dicts["fileitem"] = self.fileitem.model_dump() if self.fileitem else None
dicts["target_item"] = self.target_item.model_dump() if self.target_item else None
return dicts

View File

@@ -1,6 +1,6 @@
from typing import Optional
from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, ConfigDict
# Shared properties
@@ -22,8 +22,7 @@ class UserBase(BaseModel):
# 个性化设置
settings: Optional[dict] = Field(default_factory=dict)
class Config:
orm_mode = True
model_config = ConfigDict(from_attributes=True)
# Properties to receive via API on creation
@@ -48,8 +47,7 @@ class UserUpdate(UserBase):
class UserInDBBase(UserBase):
id: Optional[int] = None
class Config:
orm_mode = True
model_config = ConfigDict(from_attributes=True)
# Additional properties to return via API

View File

@@ -1,6 +1,6 @@
from typing import Optional, List
from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, ConfigDict
from app.schemas.context import Context, MediaInfo
from app.schemas.download import DownloadTask
@@ -29,8 +29,7 @@ class Workflow(BaseModel):
add_time: Optional[str] = Field(default=None, description="创建时间")
last_time: Optional[str] = Field(default=None, description="最后执行时间")
class Config:
orm_mode = True
model_config = ConfigDict(from_attributes=True)
class ActionParams(BaseModel):
@@ -108,5 +107,4 @@ class WorkflowShare(BaseModel):
date: Optional[str] = Field(default=None, description="分享时间")
count: Optional[int] = Field(default=0, description="复用人次")
class Config:
orm_mode = True
model_config = ConfigDict(from_attributes=True)

View File

@@ -44,7 +44,7 @@ class AddDownloadAction(BaseAction):
@classmethod
@property
def data(cls) -> dict: # noqa
return AddDownloadParams().dict()
return AddDownloadParams().model_dump()
@property
def success(self) -> bool:

View File

@@ -37,7 +37,7 @@ class AddSubscribeAction(BaseAction):
@classmethod
@property
def data(cls) -> dict: # noqa
return AddSubscribeParams().dict()
return AddSubscribeParams().model_dump()
@property
def success(self) -> bool:
@@ -57,7 +57,7 @@ class AddSubscribeAction(BaseAction):
logger.info(f"{media.title} {media.year} 已添加过订阅,跳过")
continue
mediainfo = MediaInfo()
mediainfo.from_dict(media.dict())
mediainfo.from_dict(media.model_dump())
subscribechain = SubscribeChain()
if subscribechain.exists(mediainfo):
logger.info(f"{media.title} 已存在订阅")

View File

@@ -33,7 +33,7 @@ class FetchDownloadsAction(BaseAction):
@classmethod
@property
def data(cls) -> dict: # noqa
return FetchDownloadsParams().dict()
return FetchDownloadsParams().model_dump()
@property
def success(self) -> bool:

View File

@@ -107,7 +107,7 @@ class FetchMediasAction(BaseAction):
if event and event.event_data:
event_data: RecommendSourceEventData = event.event_data
if event_data.extra_sources:
self.__inner_sources.extend([s.dict() for s in event_data.extra_sources])
self.__inner_sources.extend([s.model_dump() for s in event_data.extra_sources])
@classmethod
@property
@@ -122,7 +122,7 @@ class FetchMediasAction(BaseAction):
@classmethod
@property
def data(cls) -> dict: # noqa
return FetchMediasParams().dict()
return FetchMediasParams().model_dump()
@property
def success(self) -> bool:

View File

@@ -47,7 +47,7 @@ class FetchRssAction(BaseAction):
@classmethod
@property
def data(cls) -> dict: # noqa
return FetchRssParams().dict()
return FetchRssParams().model_dump()
@property
def success(self) -> bool:

View File

@@ -46,7 +46,7 @@ class FetchTorrentsAction(BaseAction):
@classmethod
@property
def data(cls) -> dict: # noqa
return FetchTorrentsParams().dict()
return FetchTorrentsParams().model_dump()
@property
def success(self) -> bool:

View File

@@ -39,7 +39,7 @@ class FilterMediasAction(BaseAction):
@classmethod
@property
def data(cls) -> dict: # noqa
return FilterMediasParams().dict()
return FilterMediasParams().model_dump()
@property
def success(self) -> bool:

View File

@@ -44,7 +44,7 @@ class FilterTorrentsAction(BaseAction):
@classmethod
@property
def data(cls) -> dict: # noqa
return FilterTorrentsParams().dict()
return FilterTorrentsParams().model_dump()
@property
def success(self) -> bool:

View File

@@ -37,7 +37,7 @@ class InvokePluginAction(BaseAction):
@classmethod
@property
def data(cls) -> dict: # noqa
return InvokePluginParams().dict()
return InvokePluginParams().model_dump()
@property
def success(self) -> bool:

View File

@@ -42,7 +42,7 @@ class ScanFileAction(BaseAction):
@classmethod
@property
def data(cls) -> dict: # noqa
return ScanFileParams().dict()
return ScanFileParams().model_dump()
@property
def success(self) -> bool:

View File

@@ -39,7 +39,7 @@ class ScrapeFileAction(BaseAction):
@classmethod
@property
def data(cls) -> dict: # noqa
return ScrapeFileParams().dict()
return ScrapeFileParams().model_dump()
@property
def success(self) -> bool:

View File

@@ -29,7 +29,7 @@ class SendEventAction(BaseAction):
@classmethod
@property
def data(cls) -> dict: # noqa
return SendEventParams().dict()
return SendEventParams().model_dump()
@property
def success(self) -> bool:

View File

@@ -36,7 +36,7 @@ class SendMessageAction(BaseAction):
@classmethod
@property
def data(cls) -> dict: # noqa
return SendMessageParams().dict()
return SendMessageParams().model_dump()
@property
def success(self) -> bool:

View File

@@ -44,7 +44,7 @@ class TransferFileAction(BaseAction):
@classmethod
@property
def data(cls) -> dict: # noqa
return TransferFileParams().dict()
return TransferFileParams().model_dump()
@property
def success(self) -> bool:

View File

@@ -1,5 +1,6 @@
Cython~=3.1.2
pydantic~=1.10.22
pydantic>=2.0.0,<3.0.0
pydantic-settings>=2.0.0,<3.0.0
SQLAlchemy~=2.0.41
uvicorn~=0.34.3
fastapi~=0.115.14