紡Book の勉強ノートに戻る
開発記録2026.06.25

アルバムの並び順は保存しない — 表示するたびにサーバ側で並べ直す

データ設計Django紡Book

綴Photo の web 側に「撮影日時順/綴った日順でアルバムを作る」を足したときの、サーバ側の設計の記録を勉強のためにまとめた制作メモ。

選んだ写真を時系列で流し込むページを作れるようにしたかった。撮影日時の順か、綴った日の順か、向き(古い順/新しい順)を選んで並べる、みたいなやつ。作りながら決めないと進めなかったのは、その並びを いつ確定させるか だった。作った時点で並べて固めてしまうのか、開くたびに並べ直すのか。

保存するのは「選んだ写真」と「並べ方の規則」

保存する形は2通りに整理した。

jsonc
// 手で並べる:写真IDの並びがそのまま答え
{ "template": "letter", "photoIds": ["3", "1", "2"] }
 
// 撮影日時順/綴った日順:選んだ写真 + 並べ方の規則
{
  "template": "scrollstory",
  "source": "timeline",
  "photoIds": ["3", "1", "2"],
  "rule": { "order": "taken_at", "direction": "asc" }
}
閉じても残る正は PostgreSQL の photos_templatepage.layout_json。並び結果は持たない。PostgreSQL photos_templatepage.layout_json閉じても残る。並べた結果はこの列に無い手で並べるphotoIds の順が答え3 → 1 → 2 がそのままページ日付順photoIds は材料。rule が規則並びそのものは持たない並べた結果はメモリ。表示のたびに作る。保存しない。

枚数の上限(limit)は持たせていません。ページに入る枚数 = 渡された写真の枚数。画面側で先に「何枚にしますか」と聞かない形にしたので、サーバ側にも上限が要らなくなった。

並びは表示するたびに決める

並びの確定はサーバ側でやります。

閉じても残る正は PostgreSQL の photos_templatepage.layout_json。軸は photos_photo.taken_at か photos_meaning.created_at。並び結果はメモリで保存しない。作ったときに固める並びを保存時に確定する後から1枚足すと末尾古い・中・新しい・足した日付順が崩れるlayout_json に並びごと残る閉じてもその順のまま表示するたびに並べ直す保存は材料と規則のまま後から1枚足しても正しい位置古い・足した・中・新しい日付順が保たれる軸は taken_at か最初の綴り日並び結果はメモリ。保存しない並べ直しは交差。置き場ではない。

「綴った日順」の"綴った日"はどの日か

決めないと進めない判断がもう一つあった。「綴った日順」で並べるとき、ひとつの写真に綴りが複数あったら(後から書き直したら)、どの日付で並べるのか。

決めたのは「最初に綴った日」。書き直しても並ぶ位置は動かない=最初に言葉にした順の年代記になります。

並びキーは PostgreSQL の photos_meaning で、id が最小の created_at。並べた結果はメモリで保存しない。写真 1枚(photos_photo)photos_meaning id 最小最初の綴りcreated_at が並びキー閉じても残る。基準はここだけphotos_meaning 後から書いた行書き直し並びには使わない行は残る。キーではない画面に出す件数は別。並べる基準は左の1行だけ。

これをサーバ側のクエリに落としてもらうと、こうなった。

python
# 並べ替えキー = 最初に綴った日(id が最小の Meaning の created_at)
first_written = Meaning.objects.filter(
    photo=OuterRef("pk"), owner=owner
).order_by("id").values("created_at")[:1]  # いま並べ替え中の写真の“最初の綴り”を1個だけ引く
qs = qs.annotate(_sort_key=Subquery(first_written))
# …この _sort_key で並べる(direction で昇順/降順を切り替え)

画面に何個の言葉を出すかとは別の判断で、そこは切り分けて持っておくことにした。

おわりに

綴Photo は写真も綴りも後から増えていく前提なので、固めない方が素直だった。

この記事は2026年6月時点の自分の理解で書いています。仕様や認識が変わってる可能性があります。