0

0

Django社交应用:优化关注/取关功能的ManyToManyField实践

心靈之曲

心靈之曲

发布时间:2025-08-05 12:58:17

|

998人浏览过

|

来源于php中文网

原创

django社交应用:优化关注/取关功能的manytomanyfield实践

本文旨在解决Django社交应用中关注/取关功能可能出现的重复操作问题。核心在于深入理解并正确使用ManyToManyField的symmetrical=False参数,从而简化模型设计和视图逻辑,实现高效且符合预期的单向关注关系管理,避免不必要的复杂性和数据冗余。

理解问题:Django社交关注功能中的常见陷阱

在构建社交媒体应用时,实现用户间的关注(Follow)与取关(Unfollow)功能是常见的需求。然而,不正确的模型设计和视图逻辑可能导致一系列问题,例如关注/取关操作后计数重复增加或减少,或数据逻辑混乱。

原始代码中存在两个主要问题:

  1. symmetrical='false' 的误用:在 ManyToManyField 的定义中,symmetrical 参数被错误地设置为字符串 'false',而非布尔值 False。在Python中,非空字符串被视为真值,因此 'false' 会被解析为 True,导致 ManyToManyField 默认的对称行为生效,即便你期望的是非对称关系。
  2. 冗余的 ManyToManyField 定义:为了表示关注关系,原始模型定义了两个 ManyToManyField:seguidores (粉丝) 和 seguidos (关注者)。对于一个单向的“关注”关系,通常只需要一个 ManyToManyField 配合 symmetrical=False 即可优雅地管理双向的查询。这种冗余不仅增加了代码量,也使得逻辑更容易出错。

这些问题共同导致了当用户点击关注/取关按钮时,关注计数器出现重复更新或行为不符合预期的现象。

核心概念:ManyToManyField与symmetrical参数

Django的 ManyToManyField 用于定义多对多关系。例如,一篇文章可以有多个标签,一个标签也可以关联多篇文章。在社交应用中,一个用户可以关注多个其他用户,同时也可以被多个用户关注,这正是一个典型的多对多关系。

ManyToManyField 有一个关键参数:symmetrical。

  • symmetrical=True (默认值):表示关系是对称的。如果 A 与 B 建立了关系,那么 B 也自动与 A 建立了相同的关系。这适用于“朋友”关系,即如果 A 是 B 的朋友,那么 B 也是 A 的朋友。
  • symmetrical=False:表示关系是非对称的。如果 A 与 B 建立了关系,B 不会自动与 A 建立相同的关系。这非常适合“关注”关系,即如果 A 关注了 B,B 不会自动关注 A。

当 symmetrical=False 时,Django 允许你通过 related_name 参数定义从关联模型反向查询的名称。这使得你只需定义一个 ManyToManyField 即可管理双向的非对称关系。

住哪API酒店+租车源码包
住哪API酒店+租车源码包

数据本地化解决接口缓存数据无限增加,读取慢的问题,速度极大提升更注重SEO优化优化了系统的SEO,提升网站在搜索引擎的排名,增加网站爆光率搜索框本地化不用远程读取、IFRAME调用,更加容易应用及修改增加天气预报功能页面增加了天气预报功能,丰富内容增加点评和问答页面增加了点评和问答相关页面,增强网站粘性电子地图优化优化了电子地图的加载速度与地图功能酒店列表增加房型读取酒店列表页可以直接展示房型,增

下载

优化模型设计 (models.py)

为了正确实现关注功能,我们只需要一个 ManyToManyField 来表示用户所关注的对象。通过合理设置 symmetrical=False 和 related_name,我们可以轻松地从一个用户对象获取其关注列表,以及获取关注该用户的粉丝列表。

我们将使用一个更具描述性的字段名,例如 follows,来表示用户关注了谁。

from django.contrib.auth.models import AbstractUser
from django.db import models

class User(AbstractUser):
    # 'follows' 字段表示当前用户关注了哪些其他用户。
    # 'symmetrical=False' 明确指出这是一种非对称关系(A关注B,B不一定关注A)。
    # 'related_name="followers"' 允许我们从被关注的用户(例如 user_B)
    # 反向查询到关注了 user_B 的所有用户(即 user_B 的粉丝),
    # 通过 user_B.followers.all() 来实现。
    follows = models.ManyToManyField(
        'self',
        symmetrical=False,
        related_name='followers', # 允许通过 user.followers 访问关注该用户的用户列表
        blank=True
    )

    def __str__(self):
        return self.username

    # 辅助方法,方便在模板中获取关注数和粉丝数
    @property
    def following_count(self):
        return self.follows.count() # 我关注了多少人

    @property
    def followers_count(self):
        return self.followers.count() # 有多少人关注了我

说明:

  • 我们移除了冗余的 seguidos 字段,只保留了一个 follows 字段。
  • symmetrical=False 是关键,它确保了关注关系的单向性。
  • related_name='followers' 使得我们可以通过被关注的用户实例反向查询其粉丝列表。例如,如果 user_A 关注了 user_B,那么 user_B.followers.all() 将包含 user_A。
  • 添加了 @property 装饰器的方法 following_count 和 followers_count,方便在模板中直接获取计数,提高代码可读性

精简视图逻辑 (views.py)

模型优化后,视图逻辑也将变得更加简洁和直观。我们只需要对当前登录用户的 follows 字段进行 add() 或 remove() 操作,Django ORM 会自动处理关系的建立与解除。

from django.shortcuts import render, redirect
from django.contrib.auth import get_user_model
from django.views.decorators.http import require_POST

User = get_user_model()

@require_POST # 确保只接受 POST 请求
def toggle_follow(request):
    """
    处理用户的关注/取关请求。
    """
    # 获取表单提交的动作 ('follow' 或 'unfollow') 和目标用户名
    action = request.POST.get('accion')
    target_username = request.POST.get('usuario')

    # 获取当前登录用户和目标用户对象
    logged_in_user = request.user
    try:
        target_user = User.objects.get(username=target_username)
    except User.DoesNotExist:
        # 如果目标用户不存在,可以重定向到错误页面或返回错误信息
        return redirect('index') # 假设 'index' 是主页URL

    # 检查当前用户是否已关注目标用户
    is_following = logged_in_user.follows.filter(username=target_username).exists()

    if action == 'follow':
        # 如果当前用户尚未关注目标用户,则添加关注关系
        if not is_following:
            logged_in_user.follows.add(target_user)
            print(f"{logged_in_user.username} 关注了 {target_user.username}")
    elif action == 'unfollow':
        # 如果当前用户已关注目标用户,则移除关注关系
        if is_following:
            logged_in_user.follows.remove(target_user)
            print(f"{logged_in_user.username} 取关了 {target_user.username}")
    else:
        # 处理未知动作,可以记录日志或返回错误
        print(f"未知动作: {action}")

    # 重定向

相关专题

更多
python开发工具
python开发工具

php中文网为大家提供各种python开发工具,好的开发工具,可帮助开发者攻克编程学习中的基础障碍,理解每一行源代码在程序执行时在计算机中的过程。php中文网还为大家带来python相关课程以及相关文章等内容,供大家免费下载使用。

707

2023.06.15

python打包成可执行文件
python打包成可执行文件

本专题为大家带来python打包成可执行文件相关的文章,大家可以免费的下载体验。

625

2023.07.20

python能做什么
python能做什么

python能做的有:可用于开发基于控制台的应用程序、多媒体部分开发、用于开发基于Web的应用程序、使用python处理数据、系统编程等等。本专题为大家提供python相关的各种文章、以及下载和课程。

734

2023.07.25

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

616

2023.07.31

python教程
python教程

Python已成为一门网红语言,即使是在非编程开发者当中,也掀起了一股学习的热潮。本专题为大家带来python教程的相关文章,大家可以免费体验学习。

1234

2023.08.03

python环境变量的配置
python环境变量的配置

Python是一种流行的编程语言,被广泛用于软件开发、数据分析和科学计算等领域。在安装Python之后,我们需要配置环境变量,以便在任何位置都能够访问Python的可执行文件。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

547

2023.08.04

python eval
python eval

eval函数是Python中一个非常强大的函数,它可以将字符串作为Python代码进行执行,实现动态编程的效果。然而,由于其潜在的安全风险和性能问题,需要谨慎使用。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

573

2023.08.04

scratch和python区别
scratch和python区别

scratch和python的区别:1、scratch是一种专为初学者设计的图形化编程语言,python是一种文本编程语言;2、scratch使用的是基于积木的编程语法,python采用更加传统的文本编程语法等等。本专题为大家提供scratch和python相关的文章、下载、课程内容,供大家免费下载体验。

695

2023.08.11

苹果官网入口直接访问
苹果官网入口直接访问

苹果官网直接访问入口是https://www.apple.com/cn/,该页面具备0.8秒首屏渲染、HTTP/3与Brotli加速、WebP+AVIF双格式图片、免登录浏览全参数等特性。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

10

2025.12.24

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 0.6万人学习

Django 教程
Django 教程

共28课时 | 2.4万人学习

SciPy 教程
SciPy 教程

共10课时 | 0.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号