如何使用 django 模型制作追随者系统
Posted
技术标签:
【中文标题】如何使用 django 模型制作追随者系统【英文标题】:How to make follower-following system with django model 【发布时间】: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"
],
这不是我想要的
我不知道这个问题我需要一些帮助
谢谢
【问题讨论】:
【参考方案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>
请求。在某些情况下,关注自己是可行的,尤其是如果您拥有具有多种功能的帐户以上是关于如何使用 django 模型制作追随者系统的主要内容,如果未能解决你的问题,请参考以下文章
Django 上的用户追随者模型。不能在指定中间模型的 ManyToManyField 上使用 add()。改用accounts.Contact的经理