How to make follower-following system with django

2020-07-23 03:58发布

I'm a student studying django rest framework

I'm making a simple sns with django rest framework

I need follower-following system. So, I tried to make it but there is some trouble

First this is my user model with AbstractBaseUser and 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)

the field followers is people who follows me and following is whom i follow

When i add following with this APIView class

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)})

The user_id is me and the follow is whom i want to follow

I want to add follow to user_id's following field and add user_id to follow's followers field

But it does not work

What i want for result is like this (with user information api)

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

some user's user info

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

But real result is like this

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

some user's user info

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

this is not what i want

I have no idea with this problem i need some help

Thank you

3条回答
Lonely孤独者°
2楼-- · 2020-07-23 04:09

The above solutions are fine and optimal, but I would like to supply a detailed solution for anyone who wants to implement such functionality.

The intermediary Model

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:
        unique_together = ("user_id", "following_user_id")
        ordering = ["-created"]

    def __str__(self):
        f"{self.user_id} follows {self.following_user_id}"

THE SERIALIZER for follow and unfollow

Your View for follow and unfollow

class UserFollowingViewSet(viewsets.ModelViewSet):

    permission_classes = (IsAuthenticatedOrReadOnly,)
    serializer_class = UserFollowingSerializer
    queryset = models.UserFollowing.objects.all()

Custom FollowersSerializer and FollowingSerializer

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")

Your 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
查看更多
成全新的幸福
3楼-- · 2020-07-23 04:16

I would design it in different way.

I would not add the information to the User model but explicitly create another table to store information about "followers" and "following".

Schema of the table would be:

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)

Now, in your post method implementation, you would do only this:

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

And then, you can access following and followers easily:

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

And you can then create constraint so user cannot follow the same user twice. But i leave this up to you ( hint: unique_together )

查看更多
在下西门庆
4楼-- · 2020-07-23 04:20

This is how i solved my problem.

there is a good answer above, but someone need a detail for it. so i'm writing this

I removed field followers and following in User model. And Created new model UserFollowing.

You can see this model in Enthusiast Martin's answer. I didn't use any serializer to create object.

Just two views (Follow, UnFollow) were needed.

In Follow View

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

with this we can create Following-Follower relationship.

The way to use this relationship

In User Information View

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"})

I usually use JsonResponse for response.

serializer.data and qs are user object

In UserFolowingSerializer

fields = '__all__'

I used this.

查看更多
登录 后发表回答