Item Handler
Item handler は、ItemType ごとの API を定義します。 また、sonolus.items.<item_type> から保存済みアイテムを扱う item store にアクセスできます。
パスは基本形
/sonolus/...で記載しています。APIRouter(prefix="/api")で統合した場合は/api/sonolus/...になります。
info
アイテムのinfoを提供します。
path: GET /sonolus/{item_type}/info
Registration
from sonolus_fastapi import Sonolus
from sonolus_models import ServerItemInfo
sonolus = Sonolus(
address='https://example.com',
port=8000,
enable_cors=True,
dev=True,
)
@sonolus.level.info(ServerItemInfo) # 例でlevelとしたが、level以外も可list
アイテムのlistを提供します
path: GET /sonolus/{item_type}/list
Registration
from sonolus_fastapi import Sonolus
from sonolus_models import ServerItemList
sonolus = Sonolus(
address='https://example.com',
port=8000,
enable_cors=True,
dev=True,
)
@sonolus.level.list(ServerItemList) # 例でlevelとしたが、level以外も可detail
アイテムの詳細情報を提供します
path: GET /sonolus/{item_type}/{item_name}
Registration
from sonolus_fastapi import Sonolus
from sonolus_models import ServerItemDetails
sonolus = Sonolus(
address='https://example.com',
port=8000,
enable_cors=True,
dev=True,
)
@sonolus.level.detail(ServerItemDetails) # 例でlevelとしたが、level以外も可
async def level_detail(ctx, name: str):
# nameでアイテムを取得
level = sonolus.items.level.get(name)
return ServerItemDetails(
item=level,
description="Level description",
actions=None,
hasCommunity=False,
leaderboards=[],
sections=[]
)source フィールドについて
item.sourceは保存時にストレージへ永続化されません- レスポンス返却時に
Sonolus.address(未設定時はリクエストURL)で動的上書きされます - 開発/本番でURLが変わっても、データ移行なしで柔軟に対応できます
TaggableItem
sonolus.items.<item_type>.get(name) で取得したアイテムは、TaggableItem として返されます。
TaggableItem は LevelItem や PostItem などの Pydantic モデルをラップし、tags をタイトル文字列で扱うための補助メソッドを追加します。 ラップされているだけなので、通常のアイテム属性にはそのままアクセスできます。
TIP
TaggableItem の各操作メソッドは、元のアイテムを直接書き換えず、タグを変更した新しいアイテムモデルを返します。 ストレージへ反映する場合は、返されたアイテムを update() してください。
Supported stores
TaggableItem は、item store が有効な場合に利用できます。
memoryjsondatabase
対象は sonolus.items.level.get(name) や sonolus.items.post.get(name) など、各 item store の get() です。 list() で返るアイテム一覧は通常のアイテムモデルです。
Methods
| Method | Description |
|---|---|
with_tags(tag_titles) | 指定したタグで上書きした新しいアイテムを返します |
add_tags(tag_titles) | 既存タグに追加した新しいアイテムを返します。同じタイトルのタグは重複追加されません |
remove_tags(tag_titles) | 指定したタイトルのタグを削除した新しいアイテムを返します |
clear_tags() | タグを空にした新しいアイテムを返します |
get_tag_titles() | 現在のタグタイトル一覧を返します |
tag_titles には str のリストを渡します。 内部では sonolus_models.Tag(title=...) が自動で作成されます。
Example: タグを上書きする
item = sonolus.items.post.get("example-post-1")
if item is None:
raise ValueError("Item not found")
updated_item = item.with_tags(["news", "announcement"])
sonolus.items.post.update(updated_item)Example: 既存タグに追加する
item = sonolus.items.level.get("example-level")
if item is None:
raise ValueError("Item not found")
updated_item = item.add_tags(["featured", "beginner"])
sonolus.items.level.update(updated_item)add_tags() は既存タグのタイトルを確認し、同じタイトルがある場合は追加しません。
Example: レスポンス時だけタグを付ける
ストレージには保存せず、handler のレスポンスだけタグを変更したい場合は、update() を呼ばずに返します。
from sonolus_models import ServerItemDetails
@sonolus.level.detail(ServerItemDetails)
async def level_detail(ctx, name: str):
item = sonolus.items.level.get(name)
if item is None:
raise ValueError("Item not found")
level = item.add_tags(["preview"])
return ServerItemDetails(
item=level,
description="Level description",
actions=None,
hasCommunity=False,
leaderboards=[],
sections=[],
)Example: タグの確認と削除
item = sonolus.items.post.get("example-post-1")
if item is None:
raise ValueError("Item not found")
current_tags = item.get_tag_titles()
if "draft" in current_tags:
updated_item = item.remove_tags(["draft"])
sonolus.items.post.update(updated_item)Notes
WARNING
with_tags()、add_tags()、remove_tags()、clear_tags() の戻り値は TaggableItem ではなく、通常のアイテムモデルです。 続けてタグ操作を行う場合は、保存後に再度 get() するか、返されたモデルの tags を直接扱ってください。
属性アクセスについて
TaggableItem はラップ元アイテムへの透過的な属性アクセスを提供します。
item = sonolus.items.post.get("example-post-1")
if item is not None:
print(item.name)
print(item.title)
print(item.tags)actions
アイテムのアクション(submit)を処理します
path: POST /sonolus/{item_type}/{item_name}/submit
Registration
from sonolus_fastapi import Sonolus
from sonolus_models import ServerSubmitItemActionResponse
sonolus = Sonolus(
address='https://example.com',
port=8000,
enable_cors=True,
dev=True,
)
@sonolus.level.actions(ServerSubmitItemActionResponse)
async def level_actions(ctx, name: str, action_request):
# action_requestにはフォームの値が入っている
# アクションに応じた処理を実行
return ServerSubmitItemActionResponse(
shouldUpdateItem=True,
shouldRemoveItem=False,
key="upload-key-123", # ファイルアップロードが必要な場合
hashes=[] # アップロードが必要なファイルのハッシュ
)upload
アイテムのファイルアップロードを処理します
path: POST /sonolus/{item_type}/{item_name}/upload
Registration
from sonolus_fastapi import Sonolus
from sonolus_models import ServerUploadItemActionResponse
sonolus = Sonolus(
address='https://example.com',
port=8000,
enable_cors=True,
dev=True,
)
@sonolus.level.upload(ServerUploadItemActionResponse)
async def level_upload(ctx, name: str, upload_key: str, files: list):
# upload_key: actions時に返したkey
# files: アップロードされたファイルのリスト
for file in files:
filename = file.filename
content = await file.read()
# ファイルを保存
return ServerUploadItemActionResponse(
shouldUpdateItem=True
)