Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[![Django CI](https://github.com/NoobCoder12/DevSocial/actions/workflows/django-tests.yml/badge.svg)](https://github.com/NoobCoder12/DevSocial/actions/workflows/django-tests.yml)

> Version 1.2.0
> Version 1.2.1

DevSocialApp is a social media platform designed for developers to share posts, interact with each other through likes and comments, and follow their peers.

Expand Down Expand Up @@ -132,6 +132,7 @@ docker compose up
```

The application will be accessible at `http://127.0.0.1:8000/`.
API documentation available at `http://127.0.0.1:8000/api/schema/swagger-ui/`

## Future Improvements

Expand All @@ -141,9 +142,15 @@ Things I'd add if I continue this project:
- Notifications about interactions with your post or profile
- Direct messages system
- Hashtag system for post discovery
- Pagination for feed, posts and search endpoints
- Caching with Redis for feed queries
- Deployment to AWS (ECS, RDS)

## Changelog

### v1.2.1
- Improved Swagger documentation with endpoint descriptions and parameter types

### v1.2.0
- Django Rest Framework added
- JWT Authentication for most endpoints
Expand Down
8 changes: 4 additions & 4 deletions backend/apps/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ class UserSerializer(serializers.ModelSerializer):
followers_count = serializers.SerializerMethodField() # DRF looks for a method named 'get_<field_name>'
following_count = serializers.SerializerMethodField()

def get_followers_count(self, obj):
def get_followers_count(self, obj) -> int:
return obj.followers.count()

def get_following_count(self, obj):
def get_following_count(self, obj) -> int:
return obj.following.count()

class Meta:
Expand All @@ -24,10 +24,10 @@ class PostSerializer(serializers.ModelSerializer):
likes_count = serializers.SerializerMethodField() # DRF looks for a method named 'get_<field_name>'
comments_count = serializers.SerializerMethodField()

def get_likes_count(self, obj):
def get_likes_count(self, obj) -> int:
return obj.likes_count() # Model method

def get_comments_count(self, obj):
def get_comments_count(self, obj) -> int:
return obj.comments_count()

class Meta:
Expand Down
42 changes: 36 additions & 6 deletions backend/apps/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,31 @@
)
from rest_framework import viewsets
from rest_framework import status
from drf_spectacular.utils import extend_schema, OpenApiParameter
from rest_framework.mixins import ListModelMixin


@extend_schema(exclude=True) # Do not add it to Swagger
@api_view(['GET'])
@permission_classes([AllowAny]) # Overwrites permision class for this view
def health_check(request):
return Response({'status': 'ok'})


class CurrentUserViewSet(viewsets.ReadOnlyModelViewSet):
class CurrentUserViewSet(ListModelMixin, viewsets.GenericViewSet): # RetrieveModelMixin removed
"""
Returns profile data of currently authenticated user.
"""
serializer_class = UserSerializer

def get_queryset(self):
return User.objects.filter(pk=self.request.user.pk)


class UserPostViewSet(viewsets.ModelViewSet):
"""
Manage posts of currently authenticated user.
Allows creating, reading and deleting own posts.
"""
serializer_class = PostSerializer

def get_queryset(self):
Expand All @@ -55,8 +64,8 @@ def destroy(self, request, *args, **kwargs):
# Only to read
class FeedViewSet(viewsets.ReadOnlyModelViewSet):
"""
Class allows to see posts in current user feed.
With post ID comment and like can be created
Returns posts from users that current user follows.
Use post ID to add or remove a like, or to get/add comments.
"""
serializer_class = PostSerializer

Expand All @@ -65,7 +74,12 @@ def get_queryset(self):
following = self.request.user.following.all().values_list("following_id", flat=True)
# Return posts of followed users
return Post.objects.filter(author__in=following)


# Decorator for Swagger documentation, defining data type
# 'id' is a name of variable to define
# int is a type of defined variable
# OpenApiParameter.PATH Where does it show up - in URL
@extend_schema(parameters=[OpenApiParameter('id', int, OpenApiParameter.PATH)])
# @action decorator adds action to basic URL in ViewSet
@action(detail=True, methods=['post', 'delete', 'get'])
# /like/ is created in URL from method name
Expand All @@ -82,6 +96,7 @@ def like(self, request, pk=None):
serializer = LikeSerializer(likes, many=True)
return Response(serializer.data)

@extend_schema(parameters=[OpenApiParameter('id', int, OpenApiParameter.PATH)])
@action(detail=True, methods=['post', 'get'])
def comment(self, request, pk=None):
post = self.get_object()
Expand All @@ -96,6 +111,9 @@ def comment(self, request, pk=None):


class LikeViewSet(viewsets.ReadOnlyModelViewSet):
"""
Returns all likes added by currently authenticated user.
"""
serializer_class = LikeSerializer

# Get likes of current user
Expand All @@ -108,28 +126,40 @@ def get_queryset(self):
# serializer.save(user=self.request.user)


class FollowViewSet(viewsets.ModelViewSet):
class FollowViewSet(viewsets.ReadOnlyModelViewSet):
"""
Returns follows of current user.
"""
serializer_class = FollowSerializer

def get_queryset(self):
return Follow.objects.filter(follower=self.request.user)


class CommentViewSet(viewsets.ReadOnlyModelViewSet):
"""
Returns all comments added by currently authenticated user.
"""
serializer_class = CommentSerializer

def get_queryset(self):
return Comment.objects.filter(user=self.request.user)


class SearchUsersViewSet(viewsets.ReadOnlyModelViewSet):
"""
Search for users by username.
Use query parameter 'q' to filter results. e.g. /search/?q=john
Use user ID to follow or unfollow a user.
"""
serializer_class = ProfileSerializer

def get_queryset(self):
# query_params is a dict with ULR parameters
query = self.request.query_params.get("q", "")
return Profile.objects.filter(user__username__icontains=query)

@extend_schema(parameters=[OpenApiParameter('id', int, OpenApiParameter.PATH)])
@action(detail=True, methods=['post', 'delete'])
def follow(self, request, pk=None):
followed_user = self.get_object().user # Gets user by profile
Expand Down
Loading