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

在Django REST Framework中实现关注/取关功能的最佳实践咨询

在Django REST Framework中实现关注/粉丝功能的最佳实践

我太懂这种感觉了——不用DRF的时候思路清晰得很,一结合REST框架就突然卡壳,别慌,咱们一步步把这个功能拆明白~

1. 大型项目中实现该功能的最佳方式

你的Profile模型基础设计其实很靠谱,用ManyToManyField关联用户并设置related_name='followers'正好实现双向关联,针对大型项目,还可以从这几个维度优化:

  • 数据库层优化:
    • 给following字段加上db_index=True,因为查询关注列表、粉丝列表都是高频操作,索引能大幅降低数据库查询耗时
    • 如果后期用户量突破百万级,可以考虑把关注关系抽成独立的中间模型(比如Follow),这样能额外存储关注时间、备注等信息,也方便做更复杂的统计(比如最近关注的用户)
  • 业务逻辑封装:
    • 把关注/取消关注的核心逻辑写到Profile模型的方法里,比如:
      class Profile(models.Model):
          # 你的现有字段……
          def follow_user(self, user):
              if user != self.user and user not in self.following.all():
                  self.following.add(user)
                  self.save()
                  return True
              return False
          
          def unfollow_user(self, user):
              if user in self.following.all():
                  self.following.remove(user)
                  self.save()
                  return True
              return False
      
    这样不管是在DRF视图还是其他业务场景调用,都能保证逻辑一致,还能统一处理“不能关注自己”这类校验
  • 性能优化:
    • 用select_related和prefetch_related优化查询,避免N+1问题,比如获取用户粉丝时预加载关联的Profile和User信息
    • 对关注数、粉丝数这类高频统计数据做缓存(比如用Redis),不用每次都查数据库
    • 关注/粉丝列表必须做分页处理,不然数据量大的时候接口响应会慢到离谱

2. Serializer和View的具体实现

结合你给出的Profile模型,咱们直接上可复用的代码:

Serializer部分

你需要两个Serializer:一个用于展示Profile的完整信息(包括关注数、粉丝数、当前用户是否已关注),另一个用于处理关注操作的参数校验:

from rest_framework import serializers
from django.contrib.auth import get_user_model
from .models import Profile

User = get_user_model()

class ProfileSerializer(serializers.ModelSerializer):
    # 序列化关联的用户信息(显示用户名即可)
    user = serializers.StringRelatedField()
    # 动态计算关注数、粉丝数
    following_count = serializers.SerializerMethodField()
    followers_count = serializers.SerializerMethodField()
    # 判断当前登录用户是否关注了该Profile的用户
    is_following = serializers.SerializerMethodField()

    class Meta:
        model = Profile
        # 替换成你的实际字段
        fields = ['id', 'user', 'following_count', 'followers_count', 'is_following', 'other_field1', 'other_field2']

    def get_following_count(self, obj):
        return obj.following.count()
    
    def get_followers_count(self, obj):
        # 利用related_name反向获取粉丝数
        return obj.user.followers.count()
    
    def get_is_following(self, obj):
        request = self.context.get('request')
        # 未登录用户默认返回False
        if not request or not request.user.is_authenticated:
            return False
        return obj.user in request.user.profile.following.all()

# 用于校验关注/取消关注的目标用户ID
class FollowActionSerializer(serializers.Serializer):
    user_id = serializers.IntegerField()

    def validate_user_id(self, value):
        try:
            return User.objects.get(id=value)
        except User.DoesNotExist:
            raise serializers.ValidationError("目标用户不存在")

View部分

推荐用DRF的ViewSet,既符合REST风格,又能减少重复代码。这里提供两种实现方式:

方式一:把关注/取消关注作为ProfileViewSet的自定义action

from rest_framework import viewsets, status, permissions
from rest_framework.response import Response
from .models import Profile
from .serializers import ProfileSerializer

class ProfileViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Profile.objects.all().select_related('user')
    serializer_class = ProfileSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]

    # 把request传入Serializer,用于判断is_following
    def get_serializer_context(self):
        context = super().get_serializer_context()
        context['request'] = self.request
        return context

    # 关注用户的自定义action
    @action(detail=True, methods=['post'], permission_classes=[permissions.IsAuthenticated])
    def follow(self, request, pk=None):
        target_profile = self.get_object()
        target_user = target_profile.user

        # 不能关注自己
        if target_user == request.user:
            return Response({"error": "不能关注自己"}, status=status.HTTP_400_BAD_REQUEST)
        
        # 调用Profile模型的方法执行关注逻辑
        if request.user.profile.follow_user(target_user):
            return Response({"message": "关注成功"}, status=status.HTTP_201_CREATED)
        return Response({"error": "你已经关注了该用户"}, status=status.HTTP_400_BAD_REQUEST)
    
    # 取消关注的自定义action
    @action(detail=True, methods=['delete'], permission_classes=[permissions.IsAuthenticated])
    def unfollow(self, request, pk=None):
        target_profile = self.get_object()
        target_user = target_profile.user

        if target_user == request.user:
            return Response({"error": "不能取消关注自己"}, status=status.HTTP_400_BAD_REQUEST)
        
        if request.user.profile.unfollow_user(target_user):
            return Response({"message": "取消关注成功"}, status=status.HTTP_200_OK)
        return Response({"error": "你没有关注该用户"}, status=status.HTTP_400_BAD_REQUEST)

方式二:单独的FollowView处理关注操作

如果不想和ProfileViewSet耦合,可以单独写一个ViewSet:

from rest_framework import viewsets, status, permissions
from rest_framework.response import Response
from django.contrib.auth import get_user_model
from .serializers import FollowActionSerializer
from .models import Profile

User = get_user_model()

class FollowViewSet(viewsets.ViewSet):
    permission_classes = [permissions.IsAuthenticated]

    # 关注用户
    def create(self, request):
        serializer = FollowActionSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        target_user = serializer.validated_data['user_id']

        if target_user == request.user:
            return Response({"error": "不能关注自己"}, status=status.HTTP_400_BAD_REQUEST)
        
        if request.user.profile.follow_user(target_user):
            return Response({"message": "关注成功"}, status=status.HTTP_201_CREATED)
        return Response({"error": "已关注该用户"}, status=status.HTTP_400_BAD_REQUEST)
    
    # 取消关注
    def destroy(self, request, pk=None):
        try:
            target_user = User.objects.get(id=pk)
        except User.DoesNotExist:
            return Response({"error": "目标用户不存在"}, status=status.HTTP_404_NOT_FOUND)
        
        if target_user == request.user:
            return Response({"error": "不能取消关注自己"}, status=status.HTTP_400_BAD_REQUEST)
        
        if request.user.profile.unfollow_user(target_user):
            return Response({"message": "取消关注成功"}, status=status.HTTP_200_OK)
        return Response({"error": "未关注该用户"}, status=status.HTTP_400_BAD_REQUEST)

路由配置

最后在urls.py里配置路由:

from rest_framework.routers import DefaultRouter
from .views import ProfileViewSet, FollowViewSet

router = DefaultRouter()
router.register(r'profiles', ProfileViewSet)
router.register(r'follow', FollowViewSet, basename='follow')

urlpatterns = router.urls

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:44:36