Reputation: 770
I have a user following system that works well until I try to get info if I am following the user, but it just does not work. Here is a sample of the code.
In the serializers.py
of the user, I am trying to get a check if the logged-in user follows the current user he is checking. I tried to set it to give one of 3 responses: True
, False
, or "not following"
. I am doing this in the user serializer so on the API can display some things based on the value.
The status plays a huge role here, there are three statuses, for now, followed
, unfollowed
, and blocked
. When the user follows, the status is set to followed
upon creation, when unfollowed, the user is not deleted but instead the status changes to unfollowed
, it was designed like this to save more data like time unfollowed and the rest.
When I run the code, it recognizes myself when I view my profile and returns the appropriate message, but when I am viewing a user I follow, I get the response1. in that response, since I get "not following"
instead of a True
/False
, it means that the try
block failed.
I also noticed that if in the serializer in the get_am_i_following()
function I change filter(slug=user.slug)
to filter(pk=user.pk)
it runs but I get False
(as shown in response2 below) when in fact I am supposed to get True
.
class User(AbstractBaseUser, PermissionsMixin):
username = None
email = models.EmailField(max_length=254, unique=True)
fullname = models.CharField(max_length=250)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
last_login = models.DateTimeField(null=True, blank=True)
date_joined = models.DateTimeField(auto_now_add=True)
slug = models.SlugField(max_length=255, unique=True, blank=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['fullname']
objects = UserManager()
def __str__(self):
return self.email
def follow_a_user(self, user_to_follow):
# find user by slug
try:
user_to_follow = User.objects.get(slug=user_to_follow)
except:
return False, 'User not found'
if user_to_follow != self:
try:
log = FollowLog.objects.get(
user=user_to_follow,
followed_by=self,
)
log.set_as_followed()
return True, 'Refollow successful'
except:
FollowLog.objects.create(
user=user_to_follow,
followed_by=self,
status=FollowStatus.following.value
)
return True, 'Follow Successful'
else:
return False, 'Cannot follow oneself'
def unfollow_a_user(self, user_to_unfollow):
# find user by slug
try:
user_to_unfollow = User.objects.get(slug=user_to_unfollow)
except:
return False, 'User not found'
if user_to_unfollow != self:
try:
log = FollowLog.objects.get(
user=user_to_unfollow,
followed_by=self,
)
log.set_as_unfollowed()
return True, 'UnFollow Successful'
except Exception as e:
return False, 'User doesnt follow the specified user' if 'exist' in e else e
else:
return False, 'Cannot unfollow oneself'
class FollowStatus(enum.Enum):
following = 'following'
unfollowed = 'unfollowed'
blocked = 'blocked'
FOLLOW_STATUS = (
('following', 'following'),
('unfollowed', 'unfollowed'),
('blocked', 'blocked'),
)
class FollowLog(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='followers')
followed_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE,
related_name='following', null=True)
followed_on = models.DateTimeField(auto_now_add=True)
status = models.CharField(choices=FOLLOW_STATUS, default=FollowStatus.following.value, max_length=30)
updated_on = models.DateTimeField(auto_now=True)
unfollowed_on = models.DateTimeField(null=True)
blocked_on = models.DateTimeField(null=True)
def __str__(self):
return "{} followed by {} ".format(self.user, self.followed_by)
def set_as_followed(self):
self.status = FollowStatus.following.value
self.save()
def set_as_blocked(self):
self.status = FollowStatus.blocked.value
self.blocked_on = timezone.now()
self.save()
def set_as_unfollowed(self):
self.status = FollowStatus.unfollowed.value
self.unfollowed_on = timezone.now()
self.save()
serializers.py
This is the place I am trying to make the magic happen, where I return the statements if the user(logged in) follows the other user we are checking his profile. We are doing this with the get_am_i_following
function.
class CustomUserDetailsSerializer(serializers.ModelSerializer):
'''
a custom serializer for
the user to view his own data
'''
profiles = ProfileSerializer(read_only=True, many=True)
showcase = ShowcaseSlugSerializer(read_only=True, many=True)
followers_count = serializers.SerializerMethodField(read_only=True)
following_count = serializers.SerializerMethodField(read_only=True)
am_i_following = serializers.SerializerMethodField(read_only=True)
class Meta:
model = User
fields = ('pk','email', 'fullname', 'profiles', 'slug', 'showcase', 'followers_count', 'following_count', 'am_i_following')
read_only_fields = ('email', 'fullname', 'profiles', 'slug', 'showcase')
def get_followers_count(self, instance):
return instance.followers.all().filter(status='following').count()
def get_following_count(self, instance):
return instance.following.all().filter(status='following').count()
def get_am_i_following(self, instance):
'''
returns =
(
Myself- when the user i am checking if i follow, is myself
following- when i am following user
not following- when user isnt following or when not logged in
)
'''
try:
request = self.context.get("request")
user = request.user
if user==instance:
return 'Myself'
else:
return instance.followers.filter(status='following').filter(slug=user.slug).exists()
except Exception as e:
print(e)
return "not following"
views.py
class UserRetriveAPIView(APIView):
'''
Gets a particular user in the database using the slug as the lookup
'''
serializer_class = CustomUserDetailsSerializer
permission_classes = [IsAuthenticatedOrReadOnly, IsUserOrReadOnly]
def get(self, request, slug):
user = get_object_or_404(User, slug=slug)
serializer_context = {"request": request}
serializer = self.serializer_class(user, context=serializer_context)
return Response(serializer.data, status=status.HTTP_200_OK)
urls.py
path("users/<slug:slug>/", UserRetriveAPIView.as_view(), name="users-detail")
Normally running the code when filter(slug=user.slug)
in the get_am_i_following
function gives me the JSON response where it runs the except code
{
"pk": 1,
"email": "[email protected]",
"fullname": "Opeyemi David Odedeyi",
"profiles": [
{
"id": 1,
"skills": [],
"user": "opeyemi-david-odedeyi-7ug3j0",
"date_of_birth": null,
"bio": null,
"profile_photo": null,
"sex": null,
"type_of_body": null,
"feet": null,
"inches": null,
"lives_in": null,
"updated_on": "2019-12-06T19:31:38.307863+01:00"
}
],
"slug": "opeyemi-david-odedeyi-7ug3j0",
"followers_count": 1,
"following_count": 2,
"am_i_following": "not following"
}
But changing the code when filter(slug=user.slug)
in the get_am_i_following
function gives me the JSON response but this time returns false when it is supposed to return true
{
"pk": 1,
"email": "[email protected]",
"fullname": "Opeyemi David Odedeyi",
"profiles": [
{
"id": 1,
"skills": [],
"user": "opeyemi-david-odedeyi-7ug3j0",
"date_of_birth": null,
"bio": null,
"profile_photo": null,
"sex": null,
"type_of_body": null,
"feet": null,
"inches": null,
"lives_in": null,
"updated_on": "2019-12-06T19:31:38.307863+01:00"
}
],
"slug": "opeyemi-david-odedeyi-7ug3j0",
"followers_count": 1,
"following_count": 2,
"am_i_following": false
}
[UPDATED]
And just to be sure, when I get the same users followers I get the logged in user which is the more reason it should return True
[
{
"followed_by": "olajumoke-dehinbu-3707yd"
}
]
running exceptions as e
gave me
Cannot resolve keyword 'slug' into field. Choices are: blocked_on, followed_by, followed_by_id, followed_on, id, status, unfollowed_on, updated_on, user, user_id
Upvotes: 1
Views: 131
Reputation: 659
instance.followers.filter(status='following').filter(slug=user.slug).exists()
There is your mistake. For example, with your models, if you call
instance.followers.filter(status='following')
You will get all FollowLog
models where user
field is the user we are checking his profile. And now you must check is user(logged in) follows him. But in FollowLog
model the user who follows are in followed_by
field.
So try:
instance.followers.filter(status='following').filter(fallowed_by=user).exists()
Upvotes: 2