-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
260 additions
and
52 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
from typing import Optional | ||
from django.db.models import QuerySet, Q | ||
from .models import Post, LikePost | ||
|
||
|
||
def get_all_posts() -> QuerySet[Post]: | ||
return Post.objects.all() | ||
|
||
|
||
def get_user_posts(user_id: int) -> QuerySet[Post]: | ||
return Post.objects.filter(author_id=user_id) | ||
|
||
|
||
def search_posts(query: str) -> QuerySet[Post]: | ||
return Post.objects.filter( | ||
Q(caption__icontains=query) | Q(author__first_name__icontains(query)) | ||
) | ||
|
||
|
||
def get_trending_posts(limit: int = 5) -> QuerySet[Post]: | ||
return Post.objects.order_by("-date_posted")[:limit] | ||
|
||
|
||
def get_post_by_id(post_id: int) -> Optional[Post]: | ||
try: | ||
return Post.objects.get(id=post_id) | ||
except Post.DoesNotExist: | ||
return None | ||
|
||
|
||
def get_like_for_post(user_id: int, post_id: int) -> Optional[LikePost]: | ||
try: | ||
return LikePost.objects.get(user_id=user_id, post_id=post_id) | ||
except LikePost.DoesNotExist: | ||
return None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,15 @@ | ||
from rest_framework import routers | ||
from .views import PostViewSet | ||
from .views import PostViewSet, LikePostViewSet | ||
|
||
|
||
app_name = 'apps.posts' | ||
|
||
router = routers.DefaultRouter() | ||
router.register(r'posts', PostViewSet) | ||
# router.register(r"posts", PostViewSet) | ||
# router.register(r"likes", LikePostViewSet) | ||
|
||
router.register("posts", PostViewSet, basename="posts") | ||
router.register("likes", LikePostViewSet, basename="likes") | ||
|
||
|
||
urlpatterns = router.urls |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
from rest_framework import viewsets, status, filters | ||
from rest_framework.permissions import IsAuthenticated, AllowAny | ||
from rest_framework.response import Response | ||
from rest_framework.decorators import action | ||
from django_filters.rest_framework import DjangoFilterBackend | ||
from django.core.exceptions import ObjectDoesNotExist | ||
import logging | ||
from typing import Any, Dict | ||
|
||
from .models import LikePost | ||
from .serializers import PostSerializer, LikePostSerializer | ||
from .exceptions import PostDoesNotExist | ||
from .selectors import ( | ||
get_all_posts, | ||
get_user_posts, | ||
search_posts, | ||
get_trending_posts, | ||
|
||
) | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class PostViewSet(viewsets.ModelViewSet): | ||
queryset = get_all_posts() | ||
serializer_class = PostSerializer | ||
permission_classes = [IsAuthenticated] | ||
filter_backends = [DjangoFilterBackend, filters.SearchFilter] | ||
filterset_fields = ["author"] | ||
search_fields = ["caption"] | ||
|
||
def create(self, request: Any, *args: Any, **kwargs: Any) -> Response: | ||
try: | ||
data: Dict[str, Any] = request.data | ||
data["author"] = request.user.id | ||
serializer = self.get_serializer(data=data) | ||
serializer.is_valid(raise_exception=True) | ||
self.perform_create(serializer) | ||
return Response(serializer.data, status=status.HTTP_201_CREATED) | ||
except Exception as e: | ||
logger.error(f"Error creating post: {e}") | ||
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST) | ||
|
||
@action(detail=True, methods=["post"], permission_classes=[IsAuthenticated]) | ||
def like(self, request: Any, pk: int = None) -> Response: | ||
try: | ||
post = self.get_object() | ||
like, created = LikePost.objects.get_or_create(user=request.user, post=post) | ||
if not created: | ||
like.delete() | ||
return Response({"message": "Unliked"}, status=status.HTTP_200_OK) | ||
return Response({"message": "Liked"}, status=status.HTTP_201_CREATED) | ||
except ObjectDoesNotExist: | ||
logger.error(f"Post with id {pk} does not exist") | ||
raise PostDoesNotExist() | ||
except Exception as e: | ||
logger.error(f"Error liking post: {e}") | ||
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST) | ||
|
||
@action(detail=False, methods=["get"], permission_classes=[IsAuthenticated]) | ||
def my_posts(self, request: Any) -> Response: | ||
try: | ||
queryset = get_user_posts(request.user.id) | ||
serializer = self.get_serializer(queryset, many=True) | ||
return Response(serializer.data, status=status.HTTP_200_OK) | ||
except Exception as e: | ||
logger.error(f"Error retrieving my posts: {e}") | ||
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST) | ||
|
||
@action(detail=False, methods=["get"], permission_classes=[AllowAny]) | ||
def search(self, request: Any) -> Response: | ||
try: | ||
query: str = request.query_params.get("q", "") | ||
queryset = search_posts(query) | ||
serializer = self.get_serializer(queryset, many=True) | ||
return Response(serializer.data, status=status.HTTP_200_OK) | ||
except Exception as e: | ||
logger.error(f"Error searching posts: {e}") | ||
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST) | ||
|
||
@action(detail=False, methods=["get"], permission_classes=[AllowAny]) | ||
def trending(self, request: Any) -> Response: | ||
try: | ||
queryset = get_trending_posts() | ||
serializer = self.get_serializer(queryset, many=True) | ||
return Response(serializer.data, status=status.HTTP_200_OK) | ||
except Exception as e: | ||
logger.error(f"Error retrieving trending posts: {e}") | ||
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST) | ||
|
||
|
||
class LikePostViewSet(viewsets.ModelViewSet): | ||
queryset = LikePost.objects.all() | ||
serializer_class = LikePostSerializer | ||
permission_classes = [IsAuthenticated] | ||
filter_backends = [DjangoFilterBackend] | ||
filterset_fields = ["user", "post"] | ||
|
||
def create(self, request: Any, *args: Any, **kwargs: Any) -> Response: | ||
try: | ||
data: Dict[str, Any] = request.data | ||
data["user"] = request.user.id | ||
serializer = self.get_serializer(data=data) | ||
serializer.is_valid(raise_exception=True) | ||
self.perform_create(serializer) | ||
return Response(serializer.data, status=status.HTTP_201_CREATED) | ||
except Exception as e: | ||
logger.error(f"Error creating like: {e}") | ||
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST) | ||
|
||
def destroy(self, request: Any, *args: Any, **kwargs: Any) -> Response: | ||
try: | ||
instance = self.get_object() | ||
if instance.user == request.user: | ||
self.perform_destroy(instance) | ||
return Response(status=status.HTTP_204_NO_CONTENT) | ||
return Response( | ||
{"message": "You are not allowed to delete this"}, | ||
status=status.HTTP_403_FORBIDDEN, | ||
) | ||
except ObjectDoesNotExist: | ||
logger.error(f"Like with id {kwargs['pk']} does not exist") | ||
return Response( | ||
{"message": "Like does not exist"}, status=status.HTTP_404_NOT_FOUND | ||
) | ||
except Exception as e: | ||
logger.error(f"Error deleting like: {e}") | ||
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST) | ||
|
||
def list(self, request: Any, *args: Any, **kwargs: Any) -> Response: | ||
try: | ||
queryset = self.queryset.filter(user=request.user) | ||
serializer = self.get_serializer(queryset, many=True) | ||
return Response(serializer.data, status=status.HTTP_200_OK) | ||
except Exception as e: | ||
logger.error(f"Error listing likes: {e}") | ||
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST) | ||
|
||
def retrieve(self, request: Any, *args: Any, **kwargs: Any) -> Response: | ||
try: | ||
instance = self.get_object() | ||
if instance.user == request.user: | ||
serializer = self.get_serializer(instance) | ||
return Response(serializer.data, status=status.HTTP_200_OK) | ||
return Response( | ||
{"message": "You are not allowed to view this"}, | ||
status=status.HTTP_403_FORBIDDEN, | ||
) | ||
except ObjectDoesNotExist: | ||
logger.error(f"Like with id {kwargs['pk']} does not exist") | ||
return Response( | ||
{"message": "Like does not exist"}, status=status.HTTP_404_NOT_FOUND | ||
) | ||
except Exception as e: | ||
logger.error(f"Error retrieving like: {e}") | ||
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST) | ||
|
||
def update(self, request: Any, *args: Any, **kwargs: Any) -> Response: | ||
try: | ||
instance = self.get_object() | ||
if instance.user == request.user: | ||
serializer = self.get_serializer( | ||
instance, data=request.data, partial=True | ||
) | ||
serializer.is_valid(raise_exception=True) | ||
serializer.save() | ||
return Response(serializer.data, status=status.HTTP_200_OK) | ||
return Response( | ||
{"message": "You are not allowed to update this"}, | ||
status=status.HTTP_403_FORBIDDEN, | ||
) | ||
except ObjectDoesNotExist: | ||
logger.error(f"Like with id {kwargs['pk']} does not exist") | ||
return Response( | ||
{"message": "Like does not exist"}, status=status.HTTP_404_NOT_FOUND | ||
) | ||
except Exception as e: | ||
logger |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,38 @@ | ||
""" | ||
URL configuration for config project. | ||
The `urlpatterns` list routes URLs to views. For more information please see: | ||
https://docs.djangoproject.com/en/5.0/topics/http/urls/ | ||
Examples: | ||
Function views | ||
1. Add an import: from my_app import views | ||
2. Add a URL to urlpatterns: path('', views.home, name='home') | ||
Class-based views | ||
1. Add an import: from other_app.views import Home | ||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') | ||
Including another URLconf | ||
1. Import the include() function: from django.urls import include, path | ||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) | ||
""" | ||
from django.contrib import admin | ||
from django.urls import path | ||
from django.urls import path, include | ||
from rest_framework import permissions | ||
from drf_yasg.views import get_schema_view | ||
from drf_yasg import openapi | ||
|
||
... | ||
|
||
schema_view = get_schema_view( | ||
openapi.Info( | ||
title="Snippets API", | ||
default_version="v1", | ||
description="Test description", | ||
terms_of_service="https://www.google.com/policies/terms/", | ||
contact=openapi.Contact(email="[email protected]"), | ||
license=openapi.License(name="BSD License"), | ||
), | ||
public=True, | ||
permission_classes=(permissions.AllowAny,), | ||
) | ||
|
||
urlpatterns = [ | ||
path('admin/', admin.site.urls), | ||
path("admin/", admin.site.urls), | ||
path( | ||
"swagger<format>/", schema_view.without_ui(cache_timeout=0), name="schema-json" | ||
), | ||
path( | ||
"swagger/", | ||
schema_view.with_ui("swagger", cache_timeout=0), | ||
name="schema-swagger-ui", | ||
), | ||
path("redoc/", schema_view.with_ui("redoc", cache_timeout=0), name="schema-redoc"), | ||
path("api/v1/", include("apps.posts.urls")), | ||
] | ||
|
||
admin.site.site_header = "Sharehub Admin" | ||
admin.site.site_title = "Sharehub Admin" | ||
admin.site.index_title = "Sharehub Admin" |