Django问题:点击Follow/Unfollow按钮时出现重复操作异常
问题:CS50W Project4 社交应用关注/取消关注按钮计数异常
我正在完成CS50W的Project4任务,开发一款社交应用,需要在用户个人主页添加Follow(关注)和Unfollow(取消关注)按钮。目前遇到的问题是:点击这两个按钮时,会出现关注数和粉丝数重复增减的异常情况。预期效果是:点击Follow按钮时,自己的关注数(Following)和目标用户的粉丝数(Followers)各增加1。
相关代码
views.py
def seguir(request): if request.method == 'POST': accion = request.POST.get('accion') usuario_accion = request.POST.get('usuario') usuario = request.user.username # Comprobación de si ya lo esta siguiendo y recuperación de información de usuarios perfil_usuario = User.objects.get(username=usuario) perfil_usuario_accion = User.objects.get(username=usuario_accion) esta_siguiendo = perfil_usuario.seguidos.filter(username=usuario_accion).exists() if accion == 'follow': # Si no lo esta siguiendo se añade a la lista de seguidos de uno y de seguidores del otro if esta_siguiendo == False: perfil_usuario_accion.seguidores.add(perfil_usuario) perfil_usuario.seguidos.add(perfil_usuario_accion) # Redirección a la página del usuario seguido return render(request, "network/profile.html", { "profile_user": perfil_usuario_accion, "logged_user": 0 }) else: # Comprobación de que lo siga if esta_siguiendo: perfil_usuario.seguidos.remove(perfil_usuario_accion) perfil_usuario_accion.seguidores.remove(perfil_usuario) return render(request, "network/profile.html", { "profile_user": perfil_usuario_accion, "logged_user": 0 })
profile.html
{% extends "network/layout.html" %} {% block body %} <h1 id="titulo-perfil">{{ profile_user.username }} profile page</h1> <div id="contenedor-seguir"> <div id="seguidores">Followers: {{ profile_user.seguidores.count }}</div> <div id="seguidos">Following: {{ profile_user.seguidos.count }}</div> </div> {% if logged_user == 0 %} <form id="contenedor-botones" action="{% url 'follow' %}" method="post"> {% csrf_token %} <input type="hidden" name="usuario" value="{{ profile_user.username }}"> <button type="submit" class="btn btn-primary" name="accion" value="follow">Follow</button> <button type="submit" class="btn btn-primary", name="accion" value="unfollow">Unfollow</button> </form> {% endif %} {% endblock %}
models.py
class User(AbstractUser): seguidores = models.ManyToManyField('self', symmetrical='false', related_name='tesiguen', blank=True) seguidos = models.ManyToManyField('self', symmetrical='false', related_name='siguiendo', blank=True)
问题根源及修复方案
1. 模型设计冗余,导致重复关联
你的User模型同时定义了seguidores和seguidos两个多对多字段,属于冗余设计。多对多字段的related_name已经可以实现反向关联,不需要单独维护两个字段,手动操作双字段很容易出现关联重复或不一致。
修复models.py:
只保留一个字段维护关注关系,利用反向关联自动获取粉丝列表:
class User(AbstractUser): # seguidos 表示当前用户关注的人,反向关联(粉丝)用 followers seguidos = models.ManyToManyField('self', symmetrical=False, related_name='followers', blank=True)
此时:
user.seguidos.all()→ 当前用户关注的人(Following列表)user.followers.all()→ 当前用户的粉丝(Followers列表)
2. 视图逻辑冗余+表单重复提交风险
原视图中手动同时操作两个关联字段,加上模型冗余,容易导致重复增减;另外直接render返回页面,浏览器刷新时会重复提交表单,加剧计数异常。
修复views.py:
def seguir(request): if request.method == 'POST': accion = request.POST.get('accion') usuario_accion_username = request.POST.get('usuario') try: usuario_accion = User.objects.get(username=usuario_accion_username) except User.DoesNotExist: return redirect('profile', username=usuario_accion_username) logged_user = request.user esta_siguiendo = logged_user.seguidos.filter(pk=usuario_accion.pk).exists() if accion == 'follow' and not esta_siguiendo: logged_user.seguidos.add(usuario_accion) elif accion == 'unfollow' and esta_siguiendo: logged_user.seguidos.remove(usuario_accion) # 重定向代替直接渲染,避免表单重复提交 return redirect('profile', username=usuario_accion_username)
优化点:
- 用
pk判断关注状态,比username更高效准确 - 只操作
logged_user.seguidos,反向关联的粉丝列表会自动维护 - 重定向到用户主页,彻底避免刷新重复提交问题
3. 模板按钮逻辑错误
原模板中两个按钮在同一个表单内,无法根据当前关注状态动态显示对应按钮,容易出现误提交操作。
修复profile.html:
{% extends "network/layout.html" %} {% block body %} <h1 id="titulo-perfil">{{ profile_user.username }} profile page</h1> <div id="contenedor-seguir"> <div id="seguidores">Followers: {{ profile_user.followers.count }}</div> <div id="seguidos">Following: {{ profile_user.seguidos.count }}</div> </div> {% if request.user.is_authenticated and request.user != profile_user %} <form id="contenedor-botones" action="{% url 'follow' %}" method="post"> {% csrf_token %} <input type="hidden" name="usuario" value="{{ profile_user.username }}"> {% if request.user.seguidos.filter(pk=profile_user.pk).exists %} <button type="submit" class="btn btn-primary" name="accion" value="unfollow">Unfollow</button> {% else %} <button type="submit" class="btn btn-primary" name="accion" value="follow">Follow</button> {% endif %} </form> {% endif %} {% endblock %}
改进点:
- 仅当登录用户不是目标用户本人时,才显示操作按钮
- 根据当前关注状态动态显示对应按钮,避免误操作
- 直接使用
request.user判断状态,无需传递额外变量
内容的提问来源于stack exchange,提问作者Antonio Garcia
相关产品推荐
相关产品推荐

