Skip to content

Commit

Permalink
Merge 587b6b5 into 7a97257
Browse files Browse the repository at this point in the history
  • Loading branch information
SnyderMbishai committed Dec 14, 2018
2 parents 7a97257 + 587b6b5 commit 572e861
Show file tree
Hide file tree
Showing 5 changed files with 268 additions and 4 deletions.
34 changes: 33 additions & 1 deletion authors/apps/profiles/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from django.db import models

class Profile(models.Model):
"""
Profile class.
"""
bio = models.TextField(blank=True)
avatar = models.URLField(
blank=True,
Expand All @@ -14,7 +17,36 @@ class Profile(models.Model):
'authentication.User', on_delete=models.CASCADE
)
last_update = models.DateTimeField(auto_now=True)

# Add fields for followers and following
# Followers are those that follow the user
# Following are for those that the user follows
following = models.ManyToManyField("self", related_name="user_following", symmetrical=False)

def __str__(self):
'''format the returned profile string'''
return self.user.username

def follow_author(self, author):
"""
Method for following an author.
"""
self.following.add(author)

def unfollow_author(self, author):
"""
Method for unfollowing an author.
"""
self.following.remove(author)

def retrieve_following(self):
"""
Get all the authors that the user follows.
"""
return self.following.all()

def retrieve_followers(self):
"""
Get an author's followers.
"""
return self.user_following.all()

135 changes: 135 additions & 0 deletions authors/apps/profiles/tests/test_follow_unfollow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
from django.urls import reverse
from rest_framework import status

from ..models import Profile
from authors.base_file import BaseTestCase


class TestProfileFollowUnfollow(BaseTestCase):
"""
Handle testing follow and unfollow of authors.
"""
def authenticated_user(self):
"""
Get an authenticate user details.
"""
self.client.post(self.register_url, self.register_data, format='json')
response = self.client.post(self.login_url, self.login_data, format='json')
token = 'Token ' + response.data['token']
username = response.data['username']
follow_url = reverse("profiles:follow", kwargs={'username':username})
followers_url = reverse("profiles:followers", kwargs={'username':username})
following_url = reverse("profiles:following", kwargs={'username':username})
return token, follow_url, followers_url, following_url

def create_user(self):
"""
Create a user to be used as body data.
"""
res = self.client.post(self.register_url, self.register_data2, format='json')
return res.data['data']['username']

def test_successful_author_following(self):
"""
Test successfull following of an author.
Provide valid credentials and data.
"""
details = self.authenticated_user()
url = details[1]
token = details[0]
author = {"user":self.create_user()}
response = self.client.post(url, author, format='json', HTTP_AUTHORIZATION=token)
self.assertEqual(response.status_code, status.HTTP_200_OK)

def test_following_by_unauthenticated_person(self):
"""
Test following of an author by unauthenticated person.
"""
details = self.authenticated_user()
url = details[1]
token = details[0]
author = {"user":self.create_user()}
response = self.client.post(url, author, format='json')
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

def test_following_a_non_user(self):
"""
Test following of a non-registered person.
"""
details = self.authenticated_user()
url = details[1]
token = details[0]
self.create_user()
response = self.client.post(url, {"user":"MyNameIsNoOne"}, format='json', HTTP_AUTHORIZATION=token)
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

def test_successful_unfollowing(self):
"""
Test successful unfollowing of an author.
"""
details = self.authenticated_user()
url = details[1]
token = details[0]
author = {"user":self.create_user()}
response = self.client.delete(url, author, format='json', HTTP_AUTHORIZATION=token)
self.assertEqual(response.status_code, status.HTTP_200_OK)

def test_unfollowing_by_unauthenticated_person(self):
"""
Test unauthenticated user unfollowing an author fails.
"""
details = self.authenticated_user()
url = details[1]
token = details[0]
author = {"user":self.create_user()}
response = self.client.delete(url, author, format='json')
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

def test_successful_getting_followers(self):
"""
Test successful getting of followers.
"""
details = self.authenticated_user()
url = details[2]
token = details[0]
author = {"user":self.create_user()}
self.client.post(url, author, format='json', HTTP_AUTHORIZATION=token)
response = self.client.get(url, HTTP_AUTHORIZATION=token)
self.assertEqual(response.status_code, status.HTTP_200_OK)

def test_getting_followers_without_authentication(self):
"""
Test unauthenticated user getting followers fails.
"""
details = self.authenticated_user()
url = details[2]
token = details[0]
author = {"user":self.create_user()}
self.client.delete(url, author, format='json', HTTP_AUTHORIZATION=token)
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

def test_successful_getting_following(self):
"""
Test successful getting of authors that a user follows.
"""
details = self.authenticated_user()
url = details[3]
token = details[0]
author = {"user":self.create_user()}
self.client.delete(url, author, format='json', HTTP_AUTHORIZATION=token)
response = self.client.get(url, HTTP_AUTHORIZATION=token)
self.assertEqual(response.status_code, status.HTTP_200_OK)

def test_getting_following_unauthenticated(self):
"""
Test unauthenticated user getting following fails.
"""
details = self.authenticated_user()
url = details[3]
token = details[0]
author = {"user":self.create_user()}
self.client.delete(url, author, format='json', HTTP_AUTHORIZATION=token)
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
7 changes: 5 additions & 2 deletions authors/apps/profiles/urls.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from django.urls import path
from .views import RetrieveProfileAPIView
from .views import RetrieveProfileAPIView, FollowProfileAPIView, RetrieveFollowersAPIView, RetrieveFollowingAPIView

urlpatterns = [
path('profiles/<username>/', RetrieveProfileAPIView.as_view(), name='profile')
path('profiles/<username>/', RetrieveProfileAPIView.as_view(), name='profile'),
path('profiles/<username>/follow', FollowProfileAPIView.as_view(), name='follow'),
path('profiles/<username>/followers', RetrieveFollowersAPIView.as_view(), name='followers'),
path('profiles/<username>/following', RetrieveFollowingAPIView.as_view(), name='following')
]
89 changes: 88 additions & 1 deletion authors/apps/profiles/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from rest_framework import status
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from rest_framework.generics import RetrieveAPIView, ListAPIView, RetrieveUpdateDestroyAPIView
from rest_framework.generics import RetrieveAPIView, ListAPIView, RetrieveUpdateDestroyAPIView, CreateAPIView
from .models import Profile
from .serializers import ProfileSerializer
from .exceptions import ProfileNotFound
Expand All @@ -25,3 +25,90 @@ def retrieve(self, request, username, *args, **kwargs):
'profile': serializer.data
}, status = status.HTTP_200_OK)


class FollowProfileAPIView(CreateAPIView):
"""
Class for handling following and unfollowing of authors.
"""
permission_classes = (IsAuthenticated,)
serializer_class = ProfileSerializer

def post(self, request, username):
"""
Method for following an author.
"""
user_to_follow = request.data.get('user')
current_user = request.user.profile

try:
# Check if profile to follow exists
user = Profile.objects.get(
user__username=user_to_follow
)
# Ensure current user does not follow self
if current_user == user:
return Response({'message': "You cannot follow yourself."}, status=status.HTTP_406_NOT_ACCEPTABLE)
else:
current_user.follow_author(user)
return Response({'message':"You have successfully followed '{}' ".format(user)},
status = status.HTTP_200_OK)
except Profile.DoesNotExist:
raise ProfileNotFound

def delete(self,request, username):
"""
Method for unfollowing an author.
"""
user_to_unfollow = request.data.get('user')
current_user = request.user.profile

try:
user = Profile.objects.get(
user__username=user_to_unfollow
)
current_user.unfollow_author(user)
return Response({'message':"You have successfully unfollowed '{}' ".format(user)},
status = status.HTTP_200_OK)
except Profile.DoesNotExist:
raise ProfileNotFound


class RetrieveFollowingAPIView(RetrieveAPIView):
"""
Class for handling authors that the user follows.
"""
permission_class = (IsAuthenticated,)
serializer_class = ProfileSerializer


def get(self, request, username):
"""
Retrieve all the users that a user follows.
"""
user_profile = request.user.profile
following = user_profile.retrieve_following()

serializer = ProfileSerializer(following, many=True)
message = {'message':"Authors that you follow.",
'following': serializer.data}
return Response(message, status=status.HTTP_200_OK)


class RetrieveFollowersAPIView(RetrieveAPIView):
"""
Class to handle getting followers.,
"""
permission_class = (IsAuthenticated,)
serializer_class = ProfileSerializer

def get(self, request, username):
"""
Get an author's followers.
"""
user_profile = request.user.profile
user = Profile.objects.get(user__username=username)
followers = user_profile.retrieve_followers()
serializer = self.serializer_class(followers, many=True)
message = {'message':"followers found.",
'followers': serializer.data}
return Response(message, status=status.HTTP_200_OK)
7 changes: 7 additions & 0 deletions authors/base_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ def setUp(self):
"password": "Ajakejake12#"
}}

self.register_data2 = {'user': {
"username": "JohnDoe",
"email": "joe@gmail.com",
"password": "Gokjhg12#"
}}

self.login_data = {
"user": {
"email": "jake@jake.jake",
Expand All @@ -39,3 +45,4 @@ def setUp(self):
self.token = "dummytokenhere"
self.user_url = reverse('authentication:user-retrieve-profile')
self.user_author = reverse('authentication:user-signup')

0 comments on commit 572e861

Please sign in to comment.