在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
- 把关注/取消关注的核心逻辑写到
- 性能优化:
- 用
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
相关产品推荐
相关产品推荐

