perf(core): 项目列表改轻量序列化 + 计数走真实 count,根治刷新 2-3s
/api/projects/ 列表此前与详情共用重序列化器(嵌套 阶段/片段/故事板/时间线 + 每资产算 url), 20 个项目把全套关联拉出 → 实测 ~2-3s,正是每次刷新慢的主因。 - 新增 ProjectListSerializer(轻:名/状态/阶段/product/封面/脚本数/镜数); ProjectViewSet.get_serializer_class 列表用轻的、详情用全量; get_queryset 列表走轻查询(select_related product + annotate 计数,不做那串 12 个重 prefetch)。 实测 /api/projects/ 2-3s → 0.48s。 - 新增 /api/projects/summary/(total + by_status,只跑 COUNT,不拉项目对象)。 - 计数显示(侧栏徽标 / 仪表盘 总项目·SKU·N 个)改用分页响应的真实 count(productTotal/projectTotal), 不再用「已加载 20 条」的长度;projects 列表页脚本状态/镜数改读 *_count 字段。 闸:9 页全绿、无瀑布、API 全过;projects 端点提速 ~5x。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -245,6 +245,27 @@ class ScriptVersionSerializer(serializers.ModelSerializer):
|
||||
read_only_fields = fields
|
||||
|
||||
|
||||
class ProjectListSerializer(serializers.ModelSerializer):
|
||||
"""列表/仪表盘/侧栏用的轻量项目序列化:不嵌套 阶段/片段/故事板/时间线(那些只详情页要)。
|
||||
脚本数/镜数走 annotate 计数(见 ProjectViewSet.get_queryset),避免逐项目拉全套关联(原列表 2-3s)。"""
|
||||
|
||||
product_title = serializers.CharField(source="product.title", read_only=True, default="")
|
||||
cover_preview_url = serializers.SerializerMethodField()
|
||||
script_version_count = serializers.IntegerField(read_only=True, default=0)
|
||||
video_segment_count = serializers.IntegerField(read_only=True, default=0)
|
||||
|
||||
class Meta:
|
||||
model = Project
|
||||
fields = [
|
||||
"id", "name", "product", "product_title", "cover_preview_url",
|
||||
"status", "current_stage", "script_version_count", "video_segment_count",
|
||||
"created_at", "updated_at",
|
||||
]
|
||||
|
||||
def get_cover_preview_url(self, obj) -> str:
|
||||
return _asset_preview_url(getattr(obj.product, "cover_asset", None)) if obj.product_id else ""
|
||||
|
||||
|
||||
class ProjectSerializer(serializers.ModelSerializer):
|
||||
stages = ProjectStageSerializer(many=True, read_only=True)
|
||||
video_segments = VideoSegmentSerializer(many=True, read_only=True)
|
||||
|
||||
@@ -3,6 +3,7 @@ from pathlib import Path
|
||||
import uuid
|
||||
|
||||
from django.db import transaction
|
||||
from django.db.models import Count
|
||||
from django.http import JsonResponse, StreamingHttpResponse
|
||||
from rest_framework import status
|
||||
from rest_framework.decorators import action
|
||||
@@ -54,6 +55,7 @@ from .models import (
|
||||
from .serializers import (
|
||||
BaseAssetGroupSerializer,
|
||||
ExportJobSerializer,
|
||||
ProjectListSerializer,
|
||||
ProjectSerializer,
|
||||
ScriptVersionSerializer,
|
||||
StoryboardVersionSerializer,
|
||||
@@ -133,6 +135,32 @@ class ProjectViewSet(TeamScopedViewSetMixin, ModelViewSet):
|
||||
search_fields = ["name", "product__title"]
|
||||
ordering_fields = ["created_at", "updated_at", "name"]
|
||||
|
||||
def get_serializer_class(self):
|
||||
# 列表用轻量序列化(只给列表/仪表盘/侧栏要的字段);详情/写操作用全量 ProjectSerializer
|
||||
return ProjectListSerializer if self.action == "list" else ProjectSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
# 列表:轻查询(只 select_related product + 计数注解),不做详情那串 12 个重 prefetch
|
||||
# ——原列表把每个项目的 阶段/片段/故事板/时间线/资产文件全拉出,20 个项目实测 ~2s。
|
||||
if self.action == "list":
|
||||
qs = (
|
||||
Project.objects.select_related("product", "product__cover_asset")
|
||||
.prefetch_related("product__cover_asset__files")
|
||||
.annotate(
|
||||
script_version_count=Count("script_versions", distinct=True),
|
||||
video_segment_count=Count("video_segments", distinct=True),
|
||||
)
|
||||
)
|
||||
return qs.filter(**{self.team_field: self.get_team()}).order_by("-updated_at")
|
||||
return super().get_queryset()
|
||||
|
||||
@action(detail=False, methods=["get"])
|
||||
def summary(self, request):
|
||||
"""项目统计(总数 + 各状态计数),供仪表盘/侧栏徽标——只跑 COUNT,不拉项目对象。"""
|
||||
base = Project.objects.filter(team=self.get_team())
|
||||
by_status = {row["status"]: row["n"] for row in base.values("status").annotate(n=Count("id"))}
|
||||
return Response({"total": base.count(), "by_status": by_status})
|
||||
|
||||
@transaction.atomic
|
||||
def perform_create(self, serializer):
|
||||
project = serializer.save(team=self.get_team(), created_by=self.request.user)
|
||||
|
||||
Reference in New Issue
Block a user