iam using DRF so i make a small blog that have follow/unfollow system that's my models :
class User(AbstractUser):
email = models.EmailField(unique=True)
objects = CustomUserManager()
following = models.ManyToManyField(
'self', through='Follow', symmetrical=False, related_name='followers'
)
def follow(self, target_user):
if self == target_user:
raise ValueError("You cannot follow yourself.")
if not self.is_following(target_user):
Follow.objects.create(follower=self, following=target_user)
return True
return False
def unfollow(self, target_user):
if self.is_following(target_user):
Follow.objects.filter(follower=self, following=target_user).delete()
return True
return False
def is_following(self, target_user):
return Follow.objects.filter(follower=self, following=target_user).exists()
class UserProfile(models.Model):
user = models.OneToOneField(User,on_delete = models.CASCADE, related_name='profile')
bio = models.TextField(blank=True, null=True)
profile_picture = models.ImageField(upload_to='profile_pictures/', blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
followers_count = models.IntegerField(default=0)
following_count = models.IntegerField(default=0)
# is_private = models.BooleanField(default=False)
def __str__(self):
return self.user.username
class Follow(models.Model):
follower = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user_following')
following = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user_followers')
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
constraints = [
models.UniqueConstraint(fields=['follower','following'], name="unique_followers"),
models.CheckConstraint(check=~models.Q(follower=models.F("following")), name="no_self_follow")
]
ordering = ["-created_at"]
def __str__(self) -> str:
return f"{self.follower} follows {self.following}"
and this my view :
class FollowersFollowingViewSet(ViewSet):
@action(detail=True, methods=['GET'])
def followers(self, request, pk=None):
target_user = User.objects.get(pk=pk)
followers = Follow.objects.filter(following=target_user).select_related('follower__profile','follower')
serializer = FollowersSerializer(followers, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
@action(detail=True, methods=['GET'])
def following(self, request, pk=None):
target_user = User.objects.get(pk=pk)
following = target_user.following.all()
serializer = FollowingSerializer(following, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
and those my serializers:
class FollowersSerializer(serializers.ModelSerializer):
follower = SimpleUserProfileSerializer()
class Meta:
model = Follow
fields = ['follower']
class FollowingSerializer(serializers.ModelSerializer):
following = SimpleUserProfileSerializer()
class Meta:
model = Follow
fields = ['following']
class SimpleUserProfileSerializer(serializers.ModelSerializer):
user_id = serializers.ReadOnlyField(source='user.id') # جلب user.id من العلاقة
username = serializers.ReadOnlyField(source='user.username') # جلب user.username
first_name = serializers.ReadOnlyField(source='user.first_name') # جلب user.first_name
last_name = serializers.ReadOnlyField(source='user.last_name') # جلب user.last_name
class Meta:
model = UserProfile # الموديل الأساسي هو UserProfile
fields = ['user_id', 'username', 'first_name', 'last_name', 'profile_picture', 'bio']
and this my output :
[
{
"follower": {
"profile_picture": null,
"bio": null
}
}
]
i just do not understand why he is still returning the just fields that's in profile model
i think it's because of it's a model Serializer and the model set to UserProfile ?
is there any solution or i should make a new serializer in this case ?
i just want him to get all info about all followers
following
and follower
appear to be User
objects, not UserProfile
objects, so the serializer should go the other way around:
class SimpleUserProfileSerializer(serializers.ModelSerializer):
user_id = serializers.ReadOnlyField(source='id') # جلب user.id من العلاقة
username = serializers.ReadOnlyField(source='username') # جلب user.username
first_name = serializers.ReadOnlyField(
source='first_name'
) # جلب user.first_name
last_name = serializers.ReadOnlyField(
source='last_name'
) # جلب user.last_name
bio = serializers.ReadOnlyField(source='profile.bio')
profile_picture = serializers.ReadOnlyField(source='profile.profile_picture')
class Meta:
model = User
fields = [
'user_id',
'username',
'first_name',
'last_name',
'profile_picture',
'bio',
]
Note: Please don't store aggregates in the model: determine aggregates when needed: storing aggregates in the model makes updating and keeping data in sync harder. You can use
.annotate(…)
[Django-doc] to generate counts, sums, etc. per object when needed.