Files
Ubuntu 32b43f85bd a
2026-07-03 16:58:28 +08:00

205 lines
5.5 KiB
Python

"""
Pydantic 数据验证模型
"""
from pydantic import BaseModel, Field
from typing import Optional, List
from datetime import datetime
# ==================== 认证相关 ====================
class LoginRequest(BaseModel):
username: str
password: str
class LoginResponse(BaseModel):
access_token: str
token_type: str = "bearer"
class PasswordChange(BaseModel):
old_password: str
new_password: str = Field(..., min_length=8, max_length=20)
# ==================== 文章相关 ====================
class TagBase(BaseModel):
name: str = Field(..., max_length=30)
class TagCreate(TagBase):
pass
class TagResponse(TagBase):
id: int
created_at: datetime
class Config:
from_attributes = True
class CategoryBase(BaseModel):
name: str = Field(..., max_length=50)
description: Optional[str] = Field(None, max_length=200)
class CategoryCreate(CategoryBase):
pass
class CategoryResponse(CategoryBase):
id: int
created_at: datetime
post_count: int = 0
class Config:
from_attributes = True
class PostBase(BaseModel):
title: str = Field(..., max_length=200)
slug: Optional[str] = Field(None, max_length=200)
content: str
content_type: Optional[str] = Field(default="markdown", max_length=20) # markdown 或 html
summary: Optional[str] = Field(None, max_length=500)
cover_image: Optional[str] = None
is_published: bool = True
is_draft: bool = False
category_id: Optional[int] = None
tag_ids: List[int] = []
class PostCreate(PostBase):
pass
class PostUpdate(BaseModel):
title: Optional[str] = Field(None, max_length=200)
slug: Optional[str] = Field(None, max_length=200)
content: Optional[str] = None
content_type: Optional[str] = Field(None, max_length=20)
summary: Optional[str] = Field(None, max_length=500)
cover_image: Optional[str] = None
is_published: Optional[bool] = None
is_draft: Optional[bool] = None
category_id: Optional[int] = None
tag_ids: Optional[List[int]] = None
class PostResponse(BaseModel):
id: int
title: str
slug: Optional[str] = None
content: str
content_type: Optional[str] = None
summary: Optional[str]
cover_image: Optional[str]
is_published: bool
is_draft: bool
view_count: int
created_at: datetime
updated_at: datetime
category: Optional[CategoryResponse]
tags: List[TagResponse]
class Config:
from_attributes = True
class PostListItem(BaseModel):
id: int
title: str
slug: Optional[str] = None
summary: Optional[str]
cover_image: Optional[str]
content_type: Optional[str] = None
is_published: bool
view_count: int
created_at: datetime
category: Optional[CategoryResponse]
tags: List[TagResponse]
class Config:
from_attributes = True
# ==================== 图片相关 ====================
class ImageResponse(BaseModel):
id: int
filename: str
original_name: str
description: Optional[str]
file_size: int
file_path: str
thumb_path: Optional[str] = None
width: Optional[int] = None
height: Optional[int] = None
created_at: datetime
class Config:
from_attributes = True
class ImageListResponse(BaseModel):
items: List[ImageResponse]
total: int
skip: int
limit: int
# ==================== 设置相关 ====================
class SettingBase(BaseModel):
blog_name: str = Field(..., max_length=20)
blog_description: Optional[str] = Field(None, max_length=200)
welcome_message: Optional[str] = Field(default="欢迎来到我的博客 👋 分享技术、生活与思考", max_length=300)
blog_logo: Optional[str] = None
banner_image: Optional[str] = None
banner_type: str = Field(default="text") # text或carousel
banner_images: Optional[str] = Field(default="[]") # JSON数组
theme: str = Field(default="neobrutalism")
layout: str = Field(default="card")
theme_colors: Optional[str] = Field(default='{"primary":"#7c3aed","secondary":"#06b6d4","bg":"#ffffff","text":"#1f2937","sidebar":"#1e293b"}')
class SettingUpdate(BaseModel):
blog_name: Optional[str] = Field(None, max_length=20)
blog_description: Optional[str] = Field(None, max_length=200)
welcome_message: Optional[str] = Field(None, max_length=300)
blog_logo: Optional[str] = None
banner_image: Optional[str] = None
banner_type: Optional[str] = None
banner_images: Optional[str] = None
theme: Optional[str] = None
layout: Optional[str] = None
theme_colors: Optional[str] = None
class SettingResponse(SettingBase):
id: int
updated_at: datetime
class Config:
from_attributes = True
# ==================== 页面相关 ====================
class PageBase(BaseModel):
title: str = Field(..., max_length=50)
slug: str = Field(..., max_length=50)
content: str
is_nav: bool = True
nav_order: int = 0
class PageCreate(PageBase):
pass
class PageUpdate(BaseModel):
title: Optional[str] = Field(None, max_length=50)
slug: Optional[str] = Field(None, max_length=50)
content: Optional[str] = None
is_nav: Optional[bool] = None
nav_order: Optional[int] = None
class PageResponse(PageBase):
id: int
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
# ==================== 统计相关 ====================
class StatsResponse(BaseModel):
total_posts: int
total_views: int
today_views: int
category_counts: dict
recent_posts: List[PostListItem]