add: management-команда regenerate_seria_prerender для оффлайн пересборки pre-render шаблонов серий; обновлены SETUP.md, README.md, MANAGEMENT_RUNBOOK.md

This commit is contained in:
2026-04-22 01:24:03 +03:00
parent 330737878e
commit 851babdda2
5 changed files with 312 additions and 7 deletions

View File

@@ -0,0 +1,113 @@
# -*- coding: utf-8 -*-
from __future__ import annotations
from pathlib import Path
import pytils
from django.conf import settings
from django.core.management.base import BaseCommand, CommandError
from django.db.models import F
from django.test import RequestFactory
from oknardia.models import Seria_Info
from web import catalog_series
class Command(BaseCommand):
"""Пересоздает pre-render шаблоны для страниц серий (/catalog/seria/.../all<ID>)."""
help = "Пересоздает pre-render шаблоны catalog_seria_info для выбранных или всех корневых серий."
def add_arguments(self, parser):
parser.add_argument(
"--seria-id",
type=int,
action="append",
default=[],
help="ID серии (можно передавать несколько раз). По умолчанию пересоздаются все корневые серии.",
)
parser.add_argument(
"--force",
action="store_true",
help="Пересоздать даже если pre-render файл уже существует.",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Только показать, что будет сделано, без генерации файлов.",
)
def handle(self, *args, **options):
seria_ids: list[int] = options["seria_id"]
force: bool = options["force"]
dry_run: bool = options["dry_run"]
# Берем только корневые серии, потому что для них строятся канонические URL /all<ID>.
query = Seria_Info.objects.filter(id=F("kRoot_id")).only("id", "sName").order_by("id")
if seria_ids:
query = query.filter(id__in=seria_ids)
targets = list(query)
if not targets:
raise CommandError("Не найдено подходящих корневых серий для пересоздания pre-render.")
templates_root = Path(settings.TEMPLATES[0]["DIRS"][0])
prepared_dir = templates_root / settings.PATH_FOR_SERIA_INFO_HTML_INCLUDE
prepared_dir.mkdir(parents=True, exist_ok=True)
request_factory = RequestFactory()
created = 0
planned = 0
skipped = 0
for seria in targets:
target_file = prepared_dir / f"{seria.id}_id.html"
if target_file.exists() and not force:
skipped += 1
self.stdout.write(f"SKIP {seria.id}: {target_file}")
continue
if dry_run:
action = "REGEN" if target_file.exists() else "CREATE"
self.stdout.write(f"{action} {seria.id}: {target_file}")
planned += 1
continue
if target_file.exists():
target_file.unlink()
slug = pytils.translit.slugify(seria.sName)
request = request_factory.get(f"/catalog/seria/{slug}/all{seria.id}")
# В команде принудительно включаем «production-mode» для вьюхи,
# чтобы она прошла тяжелую ветку и пересоздала pre-render файл.
old_debug = catalog_series.DEBUG
try:
catalog_series.DEBUG = False
response = catalog_series.catalog_seria_info(request, slug, seria.id)
finally:
catalog_series.DEBUG = old_debug
if response.status_code != 200:
raise CommandError(
f"Серия {seria.id}: ожидался status=200, получен {response.status_code}."
)
if not target_file.exists():
raise CommandError(f"Серия {seria.id}: pre-render файл не создан: {target_file}")
created += 1
self.stdout.write(self.style.SUCCESS(f"OK {seria.id}: {target_file}"))
if dry_run:
self.stdout.write(
self.style.SUCCESS(
f"DRY-RUN. Обработано: {len(targets)}. Будет создано/пересоздано: {planned}. Пропущено: {skipped}."
)
)
else:
self.stdout.write(
self.style.SUCCESS(
f"Готово. Обработано: {len(targets)}. Создано/пересоздано: {created}. Пропущено: {skipped}."
)
)