You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

优化Django REST Framework演员统计接口:解决数据重复与统计需求

优化Django REST Framework电影演员API方案

一、需求明确

需要实现以下三个核心功能:

  • 统计指定演员参演电影的各类型数量(格式如 {'Drama':2, 'Horror':3})
  • 解决接口返回数据随参演电影数量重复的问题
  • 获取该演员的热门类型(含名称,支持并列)、最常合作搭档的完整信息(ID、姓名、合作次数)

二、基础模型假设(基于常见电影数据集设计)

from django.db import models

class Actor(models.Model):
    id = models.IntegerField(primary_key=True)
    name = models.CharField(max_length=100)
    # 其他字段(如出生日期、简介等)

class Genre(models.Model):
    id = models.IntegerField(primary_key=True)
    name = models.CharField(max_length=50, unique=True)

class Movie(models.Model):
    id = models.IntegerField(primary_key=True)
    title = models.CharField(max_length=200)
    # 其他字段(如上映日期、评分等)

class MovieActor(models.Model):
    movie = models.ForeignKey(Movie, on_delete=models.CASCADE, related_name='actors')
    actor = models.ForeignKey(Actor, on_delete=models.CASCADE, related_name='movies')
    role = models.CharField(max_length=100, blank=True)

class MovieGenre(models.Model):
    movie = models.ForeignKey(Movie, on_delete=models.CASCADE, related_name='genres')
    genre = models.ForeignKey(Genre, on_delete=models.CASCADE, related_name='movies')

三、核心优化实现

1. 替换View解决数据重复问题

原ListAPIView会返回每条电影的重复演员数据,改用APIView返回聚合统计结果,彻底避免重复:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from django.db.models import Count
from .models import Actor, MovieActor, MovieGenre
from .serializers import ActorStatsSerializer

class ActorStatsView(APIView):
    def get(self, request, actor_id):
        try:
            actor = Actor.objects.get(id=actor_id)
        except Actor.DoesNotExist:
            return Response({"error": "演员不存在"}, status=status.HTTP_404_NOT_FOUND)
        
        # 后续统计逻辑见下文
        return Response(serializer.data, status=status.HTTP_200_OK)

2. 统计各类型电影数量

通过Django ORM的annotate和values直接关联类型名称,生成键值对格式的统计结果:

# 在ActorStatsView的get方法中添加
genre_stats = MovieGenre.objects.filter(
    movie__actors__actor=actor
).values('genre__name').annotate(
    count=Count('genre__id')
).order_by('-count')

# 转换为需求格式的字典
genre_counts = {item['genre__name']: item['count'] for item in genre_stats}

3. 获取支持并列的热门类型

先提取最大类型数量,再筛选所有符合该数量的类型:

# 在ActorStatsView的get方法中添加
top_genres = []
if genre_stats:
    max_count = genre_stats[0]['count']
    top_genres = [item['genre__name'] for item in genre_stats if item['count'] == max_count]

4. 获取最常合作搭档(含完整信息)

通过关联表统计共同参演的演员,排除自身后按合作次数排序,支持并列:

# 在ActorStatsView的get方法中添加
co_actor_stats = MovieActor.objects.filter(
    movie__actors__actor=actor
).exclude(actor=actor).values(
    'actor__id', 'actor__name'
).annotate(
    合作次数=Count('movie__id', distinct=True)
).order_by('-合作次数')

top_co_actors = []
if co_actor_stats:
    max_co_count = co_actor_stats[0]['合作次数']
    top_co_actors = [
        {
            'id': item['actor__id'],
            'name': item['actor__name'],
            '合作次数': item['合作次数']
        } for item in co_actor_stats if item['合作次数'] == max_co_count
    ]

5. 专用序列化器设计

创建针对统计结果的序列化器,避免与基础演员序列化器混淆:

from rest_framework import serializers

class CoActorSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    name = serializers.CharField()
    合作次数 = serializers.IntegerField()

class ActorStatsSerializer(serializers.Serializer):
    actor_id = serializers.IntegerField(source='id')
    actor_name = serializers.CharField(source='name')
    genre_counts = serializers.DictField(child=serializers.IntegerField())
    top_genres = serializers.ListField(child=serializers.CharField())
    top_co_actors = serializers.ListField(child=CoActorSerializer())

6. URL配置更新

替换原getActors路由,指向新的统计接口:

from django.urls import path
from .views import ActorStatsView

urlpatterns = [
    path('actors/<int:actor_id>/stats/', ActorStatsView.as_view(), name='actor-stats'),
]

四、完整View实现

整合所有逻辑后的最终View代码:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from django.db.models import Count
from .models import Actor, MovieActor, MovieGenre
from .serializers import ActorStatsSerializer

class ActorStatsView(APIView):
    def get(self, request, actor_id):
        try:
            actor = Actor.objects.get(id=actor_id)
        except Actor.DoesNotExist:
            return Response({"error": "演员不存在"}, status=status.HTTP_404_NOT_FOUND)
        
        # 统计各类型数量
        genre_stats = MovieGenre.objects.filter(
            movie__actors__actor=actor
        ).values('genre__name').annotate(
            count=Count('genre__id')
        ).order_by('-count')
        genre_counts = {item['genre__name']: item['count'] for item in genre_stats}
        
        # 获取热门类型(支持并列)
        top_genres = []
        if genre_stats:
            max_count = genre_stats[0]['count']
            top_genres = [item['genre__name'] for item in genre_stats if item['count'] == max_count]
        
        # 获取最常合作搭档
        co_actor_stats = MovieActor.objects.filter(
            movie__actors__actor=actor
        ).exclude(actor=actor).values(
            'actor__id', 'actor__name'
        ).annotate(
            合作次数=Count('movie__id', distinct=True)
        ).order_by('-合作次数')
        top_co_actors = []
        if co_actor_stats:
            max_co_count = co_actor_stats[0]['合作次数']
            top_co_actors = [
                {
                    'id': item['actor__id'],
                    'name': item['actor__name'],
                    '合作次数': item['合作次数']
                } for item in co_actor_stats if item['合作次数'] == max_co_count
            ]
        
        # 序列化返回
        serializer_data = {
            'actor_id': actor.id,
            'actor_name': actor.name,
            'genre_counts': genre_counts,
            'top_genres': top_genres,
            'top_co_actors': top_co_actors
        }
        serializer = ActorStatsSerializer(data=serializer_data)
        serializer.is_valid(raise_exception=True)
        
        return Response(serializer.data, status=status.HTTP_200_OK)

五、关键优化说明

  • 数据重复解决:放弃ListAPIView的逐条电影返回,改用聚合统计结果,彻底消除重复
  • 类型名称关联:通过values('genre__name')直接关联类型名称,避免仅返回ID的问题
  • 并列场景处理:先获取最大统计值,再筛选所有符合条件的条目,支持多个热门类型/搭档
  • 性能优化:使用distinct=True避免重复统计同一部电影的合作关系,降低数据库查询压力

内容的提问来源于stack exchange,提问作者Vagner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 15:51:03