【问题标题】:How to make follower-following system with django model如何使用 django 模型制作追随者系统
【发布时间】:2020-03-06 17:51:07
【问题描述】:

我是学习django rest框架的学生

我正在用 django rest 框架做一个简单的 sns

我需要追随者系统。所以,我试图做到,但有一些麻烦

首先这是我的带有 AbstractBaseUser 和 PermissionsMixin 的用户模型

class User(AbstractBaseUser, PermissionsMixin):
    user_id = models.CharField(max_length=100, unique=True, primary_key=True)
    name = models.CharField(max_length=100)
    created_at = models.DateTimeField(auto_now_add=True)
    is_staff = models.BooleanField(default=False)
    followers = models.ManyToManyField('self', related_name='follower',blank=True)
    following = models.ManyToManyField('self', related_name='following',blank=True)
    profile_image = models.ImageField(blank=True)

现场追随者是关注我的人,关注的是我关注的人

当我用这个 APIView 类添加以下内容时

class AddFollower(APIView):
    permission_classes = [IsAuthenticated, ]
    def post(self, requset, format=None):
        user = User.objects.get(user_id=self.request.data.get('user_id'))
        follow = User.objects.get(user_id=self.request.data.get('follow'))
        user.following.add(follow)
        user.save()
        follow.followers.add(user)
        follow.save()
        print(str(user) + ", " + str(follow))
        return JsonResponse({'status':status.HTTP_200_OK, 'data':"", 'message':"follow"+str(follow.user_id)})

user_id 是我,follow 是我想关注的人

我想将关注添加到 user_id 的关注字段并将 user_id 添加到关注的关注字段

但它不起作用

我想要的结果是这样的(带有用户信息api)

{
        "followers": [],
        "following": [
            "some user"
        ],
}

一些用户的用户信息

{
        "followers": [
            "user above"
        ],
        "following": [
        ],
}

但真正的结果是这样的

{      
        "followers": [
            "some user"
        ],
        "following": [
            "some user"
        ],
}

一些用户的用户信息

{
        "followers": [
            "user above"
        ],
        "following": [
            "user above"
        ],
}

这不是我想要的

我不知道这个问题我需要一些帮助

谢谢

【问题讨论】:

    标签: django django-models django-rest-framework


    【解决方案1】:

    我会以不同的方式设计它。

    我不会将信息添加到User 模型,而是显式创建另一个表来存储有关“关注者”和“关注者”的信息。

    表的架构将是:

    class UserFollowing(models.Model):
        user_id = models.ForeignKey("User", related_name="following")
    
        following_user_id = models.ForeignKey("User", related_name="followers")
    
        # You can even add info about when user started following
        created = models.DateTimeField(auto_now_add=True)
    

    现在,在您的 post 方法实现中,您只需要这样做:

    UserFollowing.objects.create(user_id=user.id,
                                 following_user_id=follow.id)
    

    然后,您可以轻松访问关注者和关注者:

    user = User.objects.get(id=1) # it is just example with id 1
    user.following.all()
    user.followers.all()
    

    然后您可以创建约束,这样用户就不能两次关注同一个用户。但我把这个留给你(提示:unique_together)

    【讨论】:

    • 哇!!它工作成功。谢谢!!我用你的提示阻止了两次关注同一个用户。谢谢你的好意。
    • 这是一个很好的解决方案,谢谢,但我想知道这是否是一个非常大的项目的最佳解决方案。
    • @OpeyemiOdedeyi 是的。即使是大型项目也应该没问题 - 在数据库级别,根据需要使用一些索引。然后,您需要在需要时使用查询来检索关注者/关注者。但这不是这个问题的一部分。
    • @고현석 请是否可以分享您实现此功能的示例。我仍然对此感到困惑。我想知道您的模型、序列化程序、视图是如何处理的。拜托,我真的很感激。
    • @EnthusiastMartin 是否可以分享一个您实现此功能的示例。我仍然对此感到困惑。我想知道您的模型、序列化程序、视图是如何处理的。拜托,我真的很感激。
    【解决方案2】:

    以上解决方案都很好,并且是最优的,但我想为任何想要实现此类功能的人提供详细的解决方案。

    中介模型

    from django.contrib.auth import get_user_model
    UserModel = get_user_model()
    
    class UserFollowing(models.Model):
    
        user_id = models.ForeignKey(UserModel, related_name="following", on_delete=models.CASCADE)
        following_user_id = models.ForeignKey(UserModel, related_name="followers", on_delete=models.CASCADE)
        created = models.DateTimeField(auto_now_add=True, db_index=True)
    
        class Meta:
            constraints = [
                models.UniqueConstraint(fields=['user_id','following_user_id'],  name="unique_followers")
            ]
    
            ordering = ["-created"]
    
        def __str__(self):
            f"{self.user_id} follows {self.following_user_id}"
    
    

    用于关注和取消关注的序列化器

    您的关注和取消关注视图

    class UserFollowingViewSet(viewsets.ModelViewSet):
    
        permission_classes = (IsAuthenticatedOrReadOnly,)
        serializer_class = UserFollowingSerializer
        queryset = models.UserFollowing.objects.all()
    

    自定义 FollowersSerializer 和 FollowersSerializer

    class FollowingSerializer(serializers.ModelSerializer):
    
        class Meta:
            model = UserFollowing
            fields = ("id", "following_user_id", "created")
    
    class FollowersSerializer(serializers.ModelSerializer):
        class Meta:
            model = UserFollowing
            fields = ("id", "user_id", "created")
    

    您的 UserSerializer

    from django.contrib.auth import get_user_model
    
    User = get_user_model()
    
    class UserSerializer(serializers.ModelSerializer):
    
        following = serializers.SerializerMethodField()
        followers = serializers.SerializerMethodField()
        
    
        class Meta:
            model = User
            fields = (
                "id",
                "email",
                "username",
                "following",
                "followers",
            )
            extra_kwargs = {"password": {"write_only": True}}
    
        def get_following(self, obj):
            return FollowingSerializer(obj.following.all(), many=True).data
    
        def get_followers(self, obj):
            return FollowersSerializer(obj.followers.all(), many=True).data
    

    【讨论】:

    • 它工作正常,但你没有在这里写Unfollow结构的代码。你也知道如何请求关注吗?就像我想关注一个用户,用户必须接受它?
    • 是的,这很容易,关注用户只需发送 POST 请求到 /your-following-route/ 创建模型并取消关注用户,您发送 DELETE 请求到 /your-following -路线/ID。如果您想在 Twitter 上创建待处理功能,用户需要接受您的关注请求,您可以使用一个标志来执行此操作。因此,当用户登录并接受您的关注时,您会更新模型中的标志。我希望这会有所帮助?
    • 帮助了。非常感谢!
    • 我认为你在UserFollowing: python def __str__(self): return f"{self.user_id} follows {self.following_user_id}" 中错过了return 声明
    • 感谢您的帮助。但是其中有一个问题,当我在 django admin 中签入时,用户可以自己关注。如何在 ModelViewset DRF 中发送删除请求。我需要为这些创建一个 URL 吗?
    【解决方案3】:
    models.py
    
    from django.contrib.auth.models import AbstractUser
    
    class User(AbstractUser):
        followers = models.ManyToManyField('self', symmetrical=False, 
                    blank=True)
    
        def count_followers(self):
            return self.followers.count()
        
        def count_following(self):
            return User.objects.filter(followers=self).count()
    

    【讨论】:

    • 这感觉是唯一明智的解决方案
    【解决方案4】:

    这就是我解决问题的方法。

    上面有一个很好的答案,但有人需要详细说明。所以我在写这个

    我在用户模型中删除了字段关注者和关注者。并创建了新模型 UserFollowing。

    您可以在爱好者马丁的回答中看到这个模型。我没有使用任何序列化程序来创建对象。

    只需要两个视图(关注、取消关注)。

    在关注视图中

    UserFollowing.objects.create(user_id=user.id, following_user_id=follow.id)
    

    这样我们就可以建立Following-Follower 关系。

    使用这种关系的方式

    在用户信息视图中

    following_data = UserFollowingSerializer(qs.following.all(), many=True)
    followers_data = UserFollowingSerializer(qs.followers.all(), many=True)
    return JsonResponse({'status':status.HTTP_200_OK, 'data':{'user':serializer.data, 'following':following_data.data, 'followers':followers_data.data}, "message":"success"})
    

    我通常使用 JsonResponse 来响应。

    serializer.data 和 qs 是用户对象

    在 UserFolwingSerializer 中

    fields = '__all__'
    

    我用过这个。

    【讨论】:

    • 我的一个问题是,在关注视图中,user.id 和 follow.id 是什么? user.id 是传递给我们想要关注的用户,follow.id 是传递给我们要关注的用户吗?
    • (user_id=user.id, following_user_id=follow.id) 这部分我指的是
    • 如果我是用户 A 而你是用户 B 并且我(Usre A)想关注你(用户 B)。在这种情况下,user_id 是我,following_user_id 是你
    • 兄弟,如果打扰到你,抱歉。拜托,你能看看这个:github.com/opeodedeyi/creative-api我尝试在那个应用程序中实现它,但仍然有问题。
    • 我终于明白了,我花了很长时间,但我今天终于明白了。
    【解决方案5】:

    我创建了一个类似于 Instagram 的关注和取消关注系统。

    功能:

    • 如果启用私人帐户 --> 发送关注请求。
    • 如果用户在阻止列表中 --> 他们不能关注对方用户。
    • 用户可以查看挂起的请求、已发送的请求列表、被阻止的用户 列表等。

    让我们开始吧:

    Models.py:

        class Profile(models.Model):
            user = models.OneToOneField(to = User,on_delete=models.CASCADE,related_name='profile')
              .......
            private_account = models.BooleanField(default = False)
            followers = models.ManyToManyField('self',blank=True,related_name='user_followers',symmetrical=False)
            following = models.ManyToManyField('self',blank=True,related_name='user_following',symmetrical=False)
            panding_request = models.ManyToManyField('self',blank=True,related_name='pandingRequest',symmetrical=False)
            blocked_user = models.ManyToManyField('self',blank=True,related_name='user_blocked',symmetrical=False)
            created_date = models.DateTimeField(auto_now_add=True)
            
            def __str__(self):
                return '%s' %(self.user)
    • 在这里您可以关注、取消关注、发送关注请求、接受关注请求, 拒绝请求,您可以删除您的关注者。
    • 从前端传递相反的用户配置文件ID类型 行动(关注、取消关注..)。

    views.py:

        class FollowUnfollowView(APIView):
            permission_classes = [IsAuthenticated]
            
            def current_profile(self):
                try:
                    return Profile.objects.get(user = self.request.user)
                except Profile.DoesNotExist:
                    raise Http404
                
            def other_profile(self,pk):
                try:
                    return Profile.objects.get(id = pk)
                except Profile.DoesNotExist:
                    raise Http404
            
            def post(self, request,format=None):    
                
                pk = request.data.get('id')              # Here pk is opposite user's profile ID
                req_type = request.data.get('type')        
                
                current_profile = self.current_profile()
                other_profile = self.other_profile(pk)
                
                
                if req_type == 'follow':
                    if other_profile.private_account:
                        other_profile.panding_request.add(current_profile)
                        return Response({"Requested" : "Follow request has been send!!"},status=status.HTTP_200_OK)
                    else:
                        if other_profile.blocked_user.filter(id = current_profile.id).exists():
                            return Response({"Following Fail" : "You can not follow this profile becuase your ID blocked by this user!!"},status=status.HTTP_400_BAD_REQUEST)
                        current_profile.following.add(other_profile)
                        other_profile.followers.add(current_profile)
                        return Response({"Following" : "Following success!!"},status=status.HTTP_200_OK) 
                
                elif req_type == 'accept':
                    current_profile.followers.add(other_profile)
                    other_profile.following.add(current_profile)
                    current_profile.panding_request.remove(other_profile)
                    return Response({"Accepted" : "Follow request successfuly accespted!!"},status=status.HTTP_200_OK)
                
                elif req_type == 'decline':
                    current_profile.panding_request.remove(other_profile)
                    return Response({"Decline" : "Follow request successfully declined!!"},status=status.HTTP_200_OK)
                
                elif req_type == 'unfollow':
                    current_profile.following.remove(other_profile)
                    other_profile.followers.remove(current_profile)
                    return Response({"Unfollow" : "Unfollow success!!"},status=status.HTTP_200_OK)
                    
                elif req_type == 'remove':     # You can remove your follower
                    current_profile.followers.remove(other_profile)
                    other_profile.following.remove(current_profile)
                    return Response({"Remove Success" : "Successfuly removed your follower!!"},status=status.HTTP_200_OK)
    
        # Here we can fetch followers,following detail and blocked user,pending request,sended request.. 
        
            def patch(self, request,format=None):
            
                req_type = request.data.get('type')
            
                if req_type == 'follow_detail':
                    serializer = FollowerSerializer(self.current_profile())
                    return Response({"data" : serializer.data},status=status.HTTP_200_OK)
            
                elif req_type == 'block_pending':
                    serializer = BlockPendinSerializer(self.current_profile())
                    pf = list(Profile.objects.filter(panding_request = self.current_profile().id).values('id','user__username','profile_pic','overall_pr'))
                    return Response({"data" : serializer.data,"Sended Request" :pf},status=status.HTTP_200_OK)
    
        # You can block and unblock user
    
            def put(self, request,format=None):
                pk = request.data.get('id')              # Here pk is oppisite user's profile ID
                req_type = request.data.get('type')
            
                if req_type == 'block':
                    self.current_profile().blocked_user.add(self.other_profile(pk))
                    return Response({"Blocked" : "This user blocked successfuly"},status=status.HTTP_200_OK)
                elif req_type == 'unblock':
                    self.current_profile().blocked_user.remove(self.other_profile(pk))
                    return Response({"Unblocked" : "This user unblocked successfuly"},status=status.HTTP_200_OK)
    
    

    serializers.py:

    class EachUserSerializer(serializers.ModelSerializer):
        username = serializers.CharField(source='user.username')
        class Meta:
            model = Profile
            fields = ('id','username','profile_pic')
            read_only_fields = ('id','username','profile_pic')
    
    class FollowerSerializer(serializers.ModelSerializer):
        followers = EachUserSerializer(many=True, read_only= True)
        following = EachUserSerializer(many=True,read_only=True)
        
        class Meta:
            model = Profile
            fields = ('followers','following')
            read_only_fields = ('followers','following')
    
    class BlockPendinSerializer(serializers.ModelSerializer):
        panding_request = EachUserSerializer(many=True, read_only= True)
        blocked_user = EachUserSerializer(many=True,read_only=True)
    
        class Meta:
            model = Profile
            fields = ('panding_request','blocked_user')  
            read_only_fields = ('panding_request','blocked_user')
    

    urls.py:

    from django.urls.conf import path
    from .views import *
    
    
    urlpatterns = [
        .....   
        path('follow_unfollow/',FollowUnfollowView.as_view(),name = "follow_unfollow"),
    ]
    

    如果对任何步骤有任何疑问,请发表评论。我将简要介绍一下。

    【讨论】:

      【解决方案6】:

      很好的答案。对我来说,要么是@GST Talib 的答案,要么是一个非常相似的答案,而不使用self

          following = models.ManyToManyField('User', blank=True, related_name='followers')
      

      这样,当您添加user1.following.add(user2) 时,您可以看到user2.followers 中反映的关系

      而且不需要额外的代码。

      【讨论】:

      • 这样用户可以跟随自己,如何解决这个问题。
      • 查看逻辑检查请求用户和<pk>请求。在某些情况下,关注自己是可行的,尤其是如果您拥有具有多种功能的帐户
      猜你喜欢
      • 2021-06-13
      • 2019-09-21
      • 2021-02-18
      • 1970-01-01
      • 2021-10-09
      • 1970-01-01
      • 2020-09-10
      • 2019-07-03
      • 2011-09-07
      相关资源
      最近更新 更多