Skip to content

Commit

Permalink
Following complete.
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesbrewerdev committed Aug 28, 2016
1 parent 6410b5a commit ad0bfba
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 5 deletions.
20 changes: 20 additions & 0 deletions conduit/apps/profiles/migrations/0002_profile_follows.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2016-08-28 16:09
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('profiles', '0001_initial'),
]

operations = [
migrations.AddField(
model_name='profile',
name='follows',
field=models.ManyToManyField(related_name='followed_by', to='profiles.Profile'),
),
]
27 changes: 27 additions & 0 deletions conduit/apps/profiles/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,32 @@ class Profile(TimestampedModel):
# avatar. Similar to `bio`, this field is not required. It may be blank.
image = models.URLField(blank=True)

# This is an example of a Many-To-Many relationship where both sides of the
# relationship are of the same model. In this case, the model is `Profile`.
# As mentioned in the text, this relationship will be one-way. Just because
# you are following mean does not mean that I am following you. This is
# what `symmetrical=False` does for us.
follows = models.ManyToManyField(
'self',
related_name='followed_by',
symmetrical=False
)

def __str__(self):
return self.user.username

def follow(self, profile):
"""Follow `profile` if we're not already following `profile`."""
self.follows.add(profile)

def unfollow(self, profile):
"""Unfollow `profile` if we're already following `profile`."""
self.follows.remove(profile)

def is_following(self, profile):
"""Returns True if we're following `profile`; False otherwise."""
return self.follows.filter(pk=profile.pk).exists()

def is_followed_by(self, profile):
"""Returns True if `profile` is following us; False otherwise."""
return self.followed_by.filter(pk=profile.pk).exists()
17 changes: 16 additions & 1 deletion conduit/apps/profiles/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,29 @@ class ProfileSerializer(serializers.ModelSerializer):
username = serializers.CharField(source='user.username')
bio = serializers.CharField(allow_blank=True, required=False)
image = serializers.SerializerMethodField()
following = serializers.SerializerMethodField()

class Meta:
model = Profile
fields = ('username', 'bio', 'image',)
fields = ('username', 'bio', 'image', 'following',)
read_only_fields = ('username',)

def get_image(self, obj):
if obj.image:
return obj.image

return 'https://static.productionready.io/images/smiley-cyrus.jpg'

def get_following(self, instance):
request = self.context.get('request', None)

if request is None:
return False

if not request.user.is_authenticated():
return False

follower = request.user.profile
followee = instance

return follower.is_following(followee)
4 changes: 3 additions & 1 deletion conduit/apps/profiles/urls.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from django.conf.urls import url

from .views import ProfileRetrieveAPIView
from .views import ProfileRetrieveAPIView, ProfileFollowAPIView

urlpatterns = [
url(r'^profiles/(?P<username>\w+)/?$', ProfileRetrieveAPIView.as_view()),
url(r'^profiles/(?P<username>\w+)/follow/?$',
ProfileFollowAPIView.as_view()),
]
50 changes: 47 additions & 3 deletions conduit/apps/profiles/views.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from rest_framework import status
from rest_framework import serializers, status
from rest_framework.exceptions import NotFound
from rest_framework.generics import RetrieveAPIView
from rest_framework.permissions import AllowAny
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

from .models import Profile
from .renderers import ProfileJSONRenderer
Expand All @@ -23,6 +24,49 @@ def retrieve(self, request, username, *args, **kwargs):
except Profile.DoesNotExist:
raise NotFound('A profile with this username does not exist.')

serializer = self.serializer_class(profile)
serializer = self.serializer_class(profile, context={
'request': request
})

return Response(serializer.data, status=status.HTTP_200_OK)


class ProfileFollowAPIView(APIView):
permission_classes = (IsAuthenticated,)
renderer_classes = (ProfileJSONRenderer,)
serializer_class = ProfileSerializer

def delete(self, request, username=None):
follower = self.request.user.profile

try:
followee = Profile.objects.get(user__username=username)
except Profile.DoesNotExist:
raise NotFound('A profile with this username was not found.')

follower.unfollow(followee)

serializer = self.serializer_class(followee, context={
'request': request
})

return Response(serializer.data, status=status.HTTP_200_OK)

def post(self, request, username=None):
follower = self.request.user.profile

try:
followee = Profile.objects.get(user__username=username)
except Profile.DoesNotExist:
raise NotFound('A profile with this username was not found.')

if follower.pk is followee.pk:
raise serializers.ValidationError('You can not follow yourself.')

follower.follow(followee)

serializer = self.serializer_class(followee, context={
'request': request
})

return Response(serializer.data, status=status.HTTP_201_CREATED)

0 comments on commit ad0bfba

Please sign in to comment.