diff --git a/friends/serializers.py b/friends/serializers.py index 08f1b43..13aee59 100644 --- a/friends/serializers.py +++ b/friends/serializers.py @@ -1,18 +1,135 @@ from rest_framework import serializers -from .models import Friend +from .models import Friend, FriendRequest from restaurants.models import Restaurant, UserRestaurantsList +from accounts.models import User -class FriendSerializer(serializers.ModelSerializer): +class UserSerializer(serializers.ModelSerializer): class Meta: - model = Friend - fields = "__all__" + model = User + fields = ["id", "name", "profile_img", "reliability"] class FriendRequestSerializer(serializers.ModelSerializer): + id = serializers.IntegerField(source="from_user.id") + name = serializers.CharField(source="from_user.name") + profile_img = serializers.URLField(source="from_user.profile_img.url") + reliability = serializers.IntegerField(source="from_user.reliability") + common_restaurant_count = serializers.SerializerMethodField() + + class Meta: + model = FriendRequest + fields = [ + "id", + "name", + "profile_img", + "reliability", + "common_restaurant_count", + ] + + def get_common_restaurant_count(self, obj): + try: + user = obj.from_user + # friend_user = self.context.get('request').user + friend_user = User.objects.get(id=21) + + user_restaurants = set( + UserRestaurantsList.objects.filter(user=user).values_list( + "restaurant_id", flat=True + ) + ) + friend_restaurants = set( + UserRestaurantsList.objects.filter(user=friend_user).values_list( + "restaurant_id", flat=True + ) + ) + return len(user_restaurants.intersection(friend_restaurants)) + except User.DoesNotExist: + return 0 + + +class FriendRecommendSerializer(serializers.ModelSerializer): + common_restaurant_count = serializers.SerializerMethodField() + common_restaurants = serializers.SerializerMethodField() + + class Meta: + model = User + fields = [ + "id", + "name", + "profile_img", + "reliability", + "common_restaurant_count", + "common_restaurants", + ] + + def get_common_restaurant_count(self, obj): + user = self.context.get("user") + user_restaurants = set( + UserRestaurantsList.objects.filter(user=user).values_list( + "restaurant_id", flat=True + ) + ) + friend_restaurants = set( + UserRestaurantsList.objects.filter(user=obj).values_list( + "restaurant_id", flat=True + ) + ) + return len(user_restaurants.intersection(friend_restaurants)) + + def get_common_restaurants(self, obj): + user = self.context.get("user") + user_restaurants = set( + UserRestaurantsList.objects.filter(user=user).values_list( + "restaurant_id", flat=True + ) + ) + friend_restaurants = UserRestaurantsList.objects.filter( + user=obj, restaurant_id__in=user_restaurants + ).values("restaurant__name", "restaurant__image_url")[:2] + return friend_restaurants + + def to_representation(self, instance): + representation = super().to_representation(instance) + include_restaurants = self.context.get("include_restaurants", False) + if not include_restaurants: + representation.pop("common_restaurants") + return representation + + +class FriendSerializer(serializers.ModelSerializer): + id = serializers.IntegerField(source="friend.id") + name = serializers.CharField(source="friend.name") + profile_img = serializers.URLField(source="friend.profile_img.url") + reliability = serializers.IntegerField(source="friend.reliability") + class Meta: model = Friend - fields = "__all__" + fields = [ + "id", + "name", + "profile_img", + "reliability", + ] + + +class RestaurantlistSerializer(serializers.ModelSerializer): + rating_average = serializers.SerializerMethodField() + + class Meta: + model = Restaurant + fields = [ + "id", + "name", + "food_type", + "rating_average", + "latitude", + "longitude", + "image_url", + ] + + def get_rating_average(self, obj): + return obj.rating_average() class RestaurantSerializer(serializers.ModelSerializer): @@ -23,8 +140,8 @@ class Meta: fields = "__all__" -class RestaurantSerializer(serializers.ModelSerializer): - restaurant = RestaurantSerializer() +class FriendRestaurantSerializer(serializers.ModelSerializer): + restaurant = RestaurantlistSerializer() class Meta: model = UserRestaurantsList diff --git a/friends/urls.py b/friends/urls.py index e3e2496..b4822af 100644 --- a/friends/urls.py +++ b/friends/urls.py @@ -3,10 +3,12 @@ urlpatterns = [ path( - "/restaurants/", + "friends//restaurants/", views.friend_restaurant_list, name="friend-restaurant-list", ), # 추가 기능을 테스트하기 위한 url으로 실제로는 /friend 안에서 모두 진행됨 path("friend-request/", views.FriendRequestView.as_view(), name="friend-request"), + path("friends/", views.friend_list, name="friend-list"), + path("friend-recommend/", views.friend_recommend, name="friend-recommend"), ] diff --git a/friends/views.py b/friends/views.py index 7109984..be52956 100644 --- a/friends/views.py +++ b/friends/views.py @@ -8,12 +8,20 @@ from rest_framework import status # from django.contrib.auth.decorators import login_required -from restaurants.models import UserRestaurantsList -from .serializers import RestaurantSerializer +from restaurants.models import UserRestaurantsList, Restaurant +from .serializers import ( + FriendSerializer, + FriendRequestSerializer, + RestaurantlistSerializer, + FriendRecommendSerializer, +) # from .serializers import FriendSerializer, FriendRequestSerializer from accounts.models import User from .models import Friend, FriendRequest +from django.views.decorators.csrf import csrf_exempt +from django.db.models import Count, Q +import random from django.shortcuts import get_object_or_404 @@ -27,15 +35,109 @@ def friend_restaurant_list(request, id): # 친구의 맛집 리스트를 가져옴 friend_restaurants = UserRestaurantsList.objects.filter(user=friend) - serializer = RestaurantSerializer(friend_restaurants, many=True) + restaurant_ids = friend_restaurants.values_list("restaurant_id", flat=True) + restaurants = Restaurant.objects.filter(id__in=restaurant_ids) - return Response({"restaurants": serializer.data}, status=status.HTTP_200_OK) + serializer = RestaurantlistSerializer(restaurants, many=True) + + return Response({"results": serializer.data}, status=status.HTTP_200_OK) except User.DoesNotExist: return Response( {"message": "Friend not found"}, status=status.HTTP_404_NOT_FOUND ) +@csrf_exempt +@api_view(["GET"]) +# @login_required +def friend_list(request): + try: + # user = request.user + user = User.objects.get(id=21) + + friend_request = FriendRequest.objects.filter(to_user=user, state="pending") + friend_request_serialized = FriendRequestSerializer( + friend_request, context={"request": request}, many=True + ).data + + friends = Friend.objects.filter(user=user) + friends_serialized = FriendSerializer(friends, many=True).data + + user_restaurants = set( + UserRestaurantsList.objects.filter(user=user).values_list( + "restaurant_id", flat=True + ) + ) + potential_friends = ( + User.objects.exclude(id=user.id) + .annotate( + common_restaurant_count=Count( + "userrestaurantslist__restaurant_id", + filter=Q(userrestaurantslist__restaurant_id__in=user_restaurants), + ) + ) + .order_by("-common_restaurant_count")[:7] + ) + + friend_recommend_serialized = FriendRecommendSerializer( + potential_friends, + many=True, + context={"request": request, "user": user, "include_restaurants": False}, + ).data + + data = { + "friend_request": friend_request_serialized, + "friends": friends_serialized, + "friend_recommend": friend_recommend_serialized, + } + + return Response(data) + + except User.DoesNotExist: + return Response({"message": "User not found"}, status=status.HTTP_404_NOT_FOUND) + + +@csrf_exempt +@api_view(["GET"]) +# @login_required +def friend_recommend(request): + try: + # user = request.user + user = User.objects.get(id=21) + + user_restaurants = set( + UserRestaurantsList.objects.filter(user=user).values_list( + "restaurant_id", flat=True + ) + ) + potential_friends = ( + User.objects.exclude(id=user.id) + .annotate( + common_restaurant_count=Count( + "userrestaurantslist__restaurant_id", + filter=Q(userrestaurantslist__restaurant_id__in=user_restaurants), + ) + ) + .order_by("-common_restaurant_count")[:7] + ) + + if potential_friends: + random_friend = random.choice(potential_friends) + friend_recommend_serialized = FriendRecommendSerializer( + random_friend, + context={"request": request, "user": user, "include_restaurants": True}, + ).data + return Response(friend_recommend_serialized) + + return Response( + {"message": "No recommended friends found"}, + status=status.HTTP_404_NOT_FOUND, + ) + + except User.DoesNotExist: + return Response({"message": "User not found"}, status=status.HTTP_404_NOT_FOUND) + + # 친구신청 class FriendRequestView(APIView): # authentication_classes = [TokenAuthentication]