Opeyemi Odedeyi
Opeyemi Odedeyi

Reputation: 770

Getting if a user is followed by logged in user, not working

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.

What is wrong

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.

Some things to note about how follow and unfollow works in this app

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.

What happens when I run the code below

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.

models.py
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

response1
{
    "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

Answers (1)

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

Related Questions