Add grade hot cache
This commit is contained in:
@@ -7,8 +7,8 @@ from app.models.score import GameMode
|
||||
|
||||
from .lazer_user import BASE_INCLUDES, User, UserResp
|
||||
|
||||
from pydantic import BaseModel, model_validator
|
||||
from sqlalchemy import JSON, Column, DateTime, Text
|
||||
from pydantic import BaseModel, field_validator, model_validator
|
||||
from sqlalchemy import Boolean, JSON, Column, DateTime, Text
|
||||
from sqlalchemy.ext.asyncio import AsyncAttrs
|
||||
from sqlmodel import Field, Relationship, SQLModel, col, exists, func, select
|
||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||
@@ -73,16 +73,16 @@ class BeatmapsetBase(SQLModel):
|
||||
artist_unicode: str = Field(index=True)
|
||||
covers: BeatmapCovers | None = Field(sa_column=Column(JSON))
|
||||
creator: str = Field(index=True)
|
||||
nsfw: bool = Field(default=False)
|
||||
nsfw: bool = Field(default=False, sa_column=Column(Boolean))
|
||||
play_count: int = Field(index=True)
|
||||
preview_url: str
|
||||
source: str = Field(default="")
|
||||
|
||||
spotlight: bool = Field(default=False)
|
||||
spotlight: bool = Field(default=False, sa_column=Column(Boolean))
|
||||
title: str = Field(index=True)
|
||||
title_unicode: str = Field(index=True)
|
||||
user_id: int = Field(index=True)
|
||||
video: bool = Field(index=True)
|
||||
video: bool = Field(sa_column=Column(Boolean, index=True))
|
||||
|
||||
# optional
|
||||
# converts: list[Beatmap] = Relationship(back_populates="beatmapset")
|
||||
@@ -102,13 +102,13 @@ class BeatmapsetBase(SQLModel):
|
||||
|
||||
# BeatmapsetExtended
|
||||
bpm: float = Field(default=0.0)
|
||||
can_be_hyped: bool = Field(default=False)
|
||||
discussion_locked: bool = Field(default=False)
|
||||
can_be_hyped: bool = Field(default=False, sa_column=Column(Boolean))
|
||||
discussion_locked: bool = Field(default=False, sa_column=Column(Boolean))
|
||||
last_updated: datetime = Field(sa_column=Column(DateTime, index=True))
|
||||
ranked_date: datetime | None = Field(
|
||||
default=None, sa_column=Column(DateTime, index=True)
|
||||
)
|
||||
storyboard: bool = Field(default=False, index=True)
|
||||
storyboard: bool = Field(default=False, sa_column=Column(Boolean, index=True))
|
||||
submitted_date: datetime = Field(sa_column=Column(DateTime, index=True))
|
||||
tags: str = Field(default="", sa_column=Column(Text))
|
||||
|
||||
@@ -133,7 +133,7 @@ class Beatmapset(AsyncAttrs, BeatmapsetBase, table=True):
|
||||
hype_current: int = Field(default=0)
|
||||
hype_required: int = Field(default=0)
|
||||
availability_info: str | None = Field(default=None)
|
||||
download_disabled: bool = Field(default=False)
|
||||
download_disabled: bool = Field(default=False, sa_column=Column(Boolean))
|
||||
favourites: list["FavouriteBeatmapset"] = Relationship(back_populates="beatmapset")
|
||||
|
||||
@classmethod
|
||||
@@ -205,6 +205,14 @@ class BeatmapsetResp(BeatmapsetBase):
|
||||
favourite_count: int = 0
|
||||
recent_favourites: list[UserResp] = Field(default_factory=list)
|
||||
|
||||
@field_validator('nsfw', 'spotlight', 'video', 'can_be_hyped', 'discussion_locked', 'storyboard', 'discussion_enabled', 'is_scoreable', 'has_favourited', mode='before')
|
||||
@classmethod
|
||||
def validate_bool_fields(cls, v):
|
||||
"""将整数 0/1 转换为布尔值,处理数据库中的布尔字段"""
|
||||
if isinstance(v, int):
|
||||
return bool(v)
|
||||
return v
|
||||
|
||||
@model_validator(mode="after")
|
||||
def fix_genre_language(self) -> Self:
|
||||
if self.genre is None:
|
||||
|
||||
35
app/database/field_utils.py
Normal file
35
app/database/field_utils.py
Normal file
@@ -0,0 +1,35 @@
|
||||
"""
|
||||
数据库字段类型工具
|
||||
提供处理数据库和 Pydantic 之间类型转换的工具
|
||||
"""
|
||||
from typing import Any, Union
|
||||
from pydantic import field_validator
|
||||
from sqlalchemy import Boolean
|
||||
|
||||
|
||||
def bool_field_validator(field_name: str):
|
||||
"""为特定布尔字段创建验证器,处理数据库中的 0/1 整数"""
|
||||
@field_validator(field_name, mode="before")
|
||||
@classmethod
|
||||
def validate_bool_field(cls, v: Any) -> bool:
|
||||
"""将整数 0/1 转换为布尔值"""
|
||||
if isinstance(v, int):
|
||||
return bool(v)
|
||||
return v
|
||||
return validate_bool_field
|
||||
|
||||
|
||||
def create_bool_field(**kwargs):
|
||||
"""创建一个带有正确 SQLAlchemy 列定义的布尔字段"""
|
||||
from sqlmodel import Field, Column
|
||||
|
||||
# 如果没有指定 sa_column,则使用 Boolean 类型
|
||||
if 'sa_column' not in kwargs:
|
||||
# 处理 index 参数
|
||||
index = kwargs.pop('index', False)
|
||||
if index:
|
||||
kwargs['sa_column'] = Column(Boolean, index=True)
|
||||
else:
|
||||
kwargs['sa_column'] = Column(Boolean)
|
||||
|
||||
return Field(**kwargs)
|
||||
@@ -45,8 +45,9 @@ from .relationship import (
|
||||
)
|
||||
from .score_token import ScoreToken
|
||||
|
||||
from pydantic import field_validator
|
||||
from redis.asyncio import Redis
|
||||
from sqlalchemy import Column, ColumnExpressionArgument, DateTime
|
||||
from sqlalchemy import Boolean, Column, ColumnExpressionArgument, DateTime
|
||||
from sqlalchemy.ext.asyncio import AsyncAttrs
|
||||
from sqlalchemy.orm import aliased
|
||||
from sqlalchemy.sql.elements import ColumnElement
|
||||
@@ -79,13 +80,13 @@ class ScoreBase(AsyncAttrs, SQLModel, UTCBaseModel):
|
||||
default=0, sa_column=Column(BigInteger)
|
||||
) # solo_score
|
||||
ended_at: datetime = Field(sa_column=Column(DateTime))
|
||||
has_replay: bool
|
||||
has_replay: bool = Field(sa_column=Column(Boolean))
|
||||
max_combo: int
|
||||
mods: list[APIMod] = Field(sa_column=Column(JSON))
|
||||
passed: bool
|
||||
passed: bool = Field(sa_column=Column(Boolean))
|
||||
playlist_item_id: int | None = Field(default=None) # multiplayer
|
||||
pp: float = Field(default=0.0)
|
||||
preserve: bool = Field(default=True)
|
||||
preserve: bool = Field(default=True, sa_column=Column(Boolean))
|
||||
rank: Rank
|
||||
room_id: int | None = Field(default=None) # multiplayer
|
||||
started_at: datetime = Field(sa_column=Column(DateTime))
|
||||
@@ -176,6 +177,14 @@ class ScoreResp(ScoreBase):
|
||||
ranked: bool = False
|
||||
current_user_attributes: CurrentUserAttributes | None = None
|
||||
|
||||
@field_validator('has_replay', 'passed', 'preserve', 'is_perfect_combo', 'legacy_perfect', 'processed', 'ranked', mode='before')
|
||||
@classmethod
|
||||
def validate_bool_fields(cls, v):
|
||||
"""将整数 0/1 转换为布尔值,处理数据库中的布尔字段"""
|
||||
if isinstance(v, int):
|
||||
return bool(v)
|
||||
return v
|
||||
|
||||
@classmethod
|
||||
async def from_db(cls, session: AsyncSession, score: Score) -> "ScoreResp":
|
||||
s = cls.model_validate(score.model_dump())
|
||||
|
||||
Reference in New Issue
Block a user