diff --git a/accounts/admin.py b/accounts/admin.py index 4185d36..c4a654c 100644 --- a/accounts/admin.py +++ b/accounts/admin.py @@ -1,3 +1,4 @@ -# from django.contrib import admin +from django.contrib import admin +from .models import User -# Register your models here. +admin.site.register(User) diff --git a/accounts/models.py b/accounts/models.py index 4dff485..6428c7f 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -19,7 +19,7 @@ class User(AbstractUser): reliability = models.SmallIntegerField(default=100) USERNAME_FIELD = "email" - REQUIRED_FIELDS = ["name"] + REQUIRED_FIELDS = ["name", "username"] def __str__(self): return self.name diff --git a/friends/serializers.py b/friends/serializers.py index a547dbc..7851345 100644 --- a/friends/serializers.py +++ b/friends/serializers.py @@ -1,8 +1,74 @@ from rest_framework import serializers from .models import Friend +from restaurants.models import Restaurant, UserRestaurantsList +from accounts.models import User + + +class UserSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = ["id", "name", "profile_img", "reliability"] + + +class FriendRequestSerializer(serializers.ModelSerializer): + user = UserSerializer(read_only=True) + common_restaurant_count = serializers.SerializerMethodField() + + class Meta: + model = Friend + fields = ["user", "state", "common_restaurant_count"] + + def get_common_restaurant_count(self, obj): + try: + user = obj.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 FriendSerializer(serializers.ModelSerializer): + friend = UserSerializer(read_only=True) + class Meta: model = Friend + fields = ["friend", "state"] + + +class RestaurantlistSerializer(serializers.ModelSerializer): + rating_average = serializers.SerializerMethodField() + + class Meta: + model = Restaurant + fields = ["id", "name", "food_type", "rating_average", "latitude", "longitude"] + + def get_rating_average(self, obj): + return obj.rating_average() + + +class RestaurantSerializer(serializers.ModelSerializer): + # reviews = serializers.SerializerMethodField() + + class Meta: + model = Restaurant + fields = "__all__" + + +class FriendRestaurantSerializer(serializers.ModelSerializer): + restaurant = RestaurantlistSerializer() + + class Meta: + model = UserRestaurantsList fields = "__all__" diff --git a/friends/urls.py b/friends/urls.py new file mode 100644 index 0000000..d504dba --- /dev/null +++ b/friends/urls.py @@ -0,0 +1,11 @@ +from django.urls import path +from . import views + +urlpatterns = [ + path( + "friends//restaurants/", + views.friend_restaurant_list, + name="friend-restaurant-list", + ), + path("friends/", views.friend_list, name="friend-list"), +] diff --git a/friends/views.py b/friends/views.py index fd0e044..e6b551b 100644 --- a/friends/views.py +++ b/friends/views.py @@ -1,3 +1,59 @@ # from django.shortcuts import render +from rest_framework.decorators import api_view +from rest_framework.response import Response +from rest_framework import status -# Create your views here. +# from django.contrib.auth.decorators import login_required +from restaurants.models import UserRestaurantsList +from .serializers import ( + FriendSerializer, + FriendRequestSerializer, + FriendRestaurantSerializer, +) +from accounts.models import User +from .models import Friend +from django.views.decorators.csrf import csrf_exempt + + +@api_view(["GET"]) +# @login_required +def friend_restaurant_list(request, id): + try: + # id에 해당하는 친구를 가져옴 + friend = User.objects.get(id=id) + + # 친구의 맛집 리스트를 가져옴 + friend_restaurants = UserRestaurantsList.objects.filter(user=friend) + serializer = FriendRestaurantSerializer(friend_restaurants, many=True) + + return Response({"restaurants": 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 = User.objects.get(id=21) + + friend_request = Friend.objects.filter(friend=user, state="request") + friend_request_serialized = FriendRequestSerializer( + friend_request, context={"request": request}, many=True + ).data + + friends = Friend.objects.filter(user=user, state="approve") + friends_serialized = FriendSerializer(friends, many=True).data + + data = { + "friend_request": friend_request_serialized, + "friends": friends_serialized, + } + + return Response(data) + + except User.DoesNotExist: + return Response({"message": "User not found"}, status=status.HTTP_404_NOT_FOUND) diff --git a/mustgou/settings.py b/mustgou/settings.py index 7e9b5a9..05a74bc 100644 --- a/mustgou/settings.py +++ b/mustgou/settings.py @@ -175,3 +175,5 @@ # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" + +LOGIN_URL = "/auth/login/" diff --git a/mustgou/urls.py b/mustgou/urls.py index 5945c7a..c7ec8dd 100644 --- a/mustgou/urls.py +++ b/mustgou/urls.py @@ -21,4 +21,5 @@ path("admin/", admin.site.urls), path("", include("restaurants.urls")), path("auth/", include("accounts.urls")), + path("", include("friends.urls")), ] diff --git a/restaurants/migrations/0005_restaurant_food_type.py b/restaurants/migrations/0005_restaurant_food_type.py new file mode 100644 index 0000000..769417c --- /dev/null +++ b/restaurants/migrations/0005_restaurant_food_type.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.14 on 2024-08-03 07:13 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("restaurants", "0004_userrestaurantslist"), + ] + + operations = [ + migrations.AddField( + model_name="restaurant", + name="food_type", + field=models.CharField(blank=True, max_length=20, null=True), + ), + ] diff --git a/restaurants/models.py b/restaurants/models.py index 41bed37..522e095 100644 --- a/restaurants/models.py +++ b/restaurants/models.py @@ -6,6 +6,7 @@ class Restaurant(models.Model): name = models.CharField(max_length=255) + food_type = models.CharField(max_length=20, null=True, blank=True) rating_naver = models.DecimalField( max_digits=5, decimal_places=2, null=True, blank=True ) @@ -24,7 +25,7 @@ def rating_average(self): valid_ratings = [rating for rating in ratings if rating is not None] if valid_ratings: return sum(valid_ratings) / len(valid_ratings) - return None + return 0 class SearchHistory(models.Model): diff --git a/restaurants/serializers.py b/restaurants/serializers.py index 8cb0236..74fcfca 100644 --- a/restaurants/serializers.py +++ b/restaurants/serializers.py @@ -1,19 +1,18 @@ from rest_framework import serializers from .models import Restaurant, SearchHistory, UserRestaurantsList + from reviews.serializers import ReviewSerializer class RestaurantSerializer(serializers.ModelSerializer): - reviews = serializers.SerializerMethodField() + rating_average = serializers.SerializerMethodField() class Meta: model = Restaurant fields = "__all__" - def get_reviews(self, obj): - reviews = obj.reviews.order_by("-recommend_count")[:4] - serializer = ReviewSerializer(reviews, many=True) - return serializer.data + def get_rating_average(self, obj): + return obj.rating_average() class RestaurantListSerializer(serializers.ModelSerializer): @@ -21,21 +20,53 @@ class RestaurantListSerializer(serializers.ModelSerializer): class Meta: model = Restaurant - fields = ["name", "rating_average", "address"] + fields = [ + "name", + "rating_average", + "address", + ] def get_rating_average(self, obj): - return obj.rating_average + return obj.rating_average() class SearchHistorySerializer(serializers.ModelSerializer): class Meta: model = SearchHistory - fields = ["query", "timestamp"] + fields = ["id", "query", "timestamp"] + + +class RestaurantlistSerializer(serializers.ModelSerializer): + rating_average = serializers.SerializerMethodField() + + class Meta: + model = Restaurant + fields = ["id", "name", "food_type", "rating_average", "latitude", "longitude"] + + def get_rating_average(self, obj): + return obj.rating_average() class UserRestaurantListSerializer(serializers.ModelSerializer): - restaurant = RestaurantSerializer() + restaurant = RestaurantlistSerializer() class Meta: model = UserRestaurantsList fields = "__all__" + + +class RestaurantDetailSerializer(serializers.ModelSerializer): + reviews = serializers.SerializerMethodField() + rating_average = serializers.SerializerMethodField() + + class Meta: + model = Restaurant + fields = "__all__" + + def get_reviews(self, obj): + reviews = obj.reviews.order_by("-recommend_count")[:4] + serializer = ReviewSerializer(reviews, many=True) + return serializer.data + + def get_rating_average(self, obj): + return obj.rating_average() diff --git a/restaurants/urls.py b/restaurants/urls.py index 3d5ce2d..4d81551 100644 --- a/restaurants/urls.py +++ b/restaurants/urls.py @@ -3,7 +3,7 @@ urlpatterns = [ - # path("restaurants/", views.restaurant_list, name="restaurant-list"), + # path("restaurants/", views.restaurant_list, name="restaurant-list"), path("api-auth/", include("rest_framework.urls", namespace="rest_framework")), path("search/", views.search, name="search"), path( diff --git a/restaurants/views.py b/restaurants/views.py index a035c63..6885cb7 100644 --- a/restaurants/views.py +++ b/restaurants/views.py @@ -8,10 +8,16 @@ RestaurantListSerializer, SearchHistorySerializer, UserRestaurantListSerializer, + RestaurantDetailSerializer, ) -from django.contrib.auth.decorators import login_required +# from django.contrib.auth.decorators import login_required +from django.views.decorators.csrf import csrf_exempt +import logging +from accounts.models import User # 임시 유저 지정을 위한 임포트, 추후 삭제 + +@csrf_exempt @api_view(["GET"]) def restaurant_list(request): restaurants = Restaurant.objects.all() @@ -19,11 +25,14 @@ def restaurant_list(request): return Response(serializer.data) -@api_view(["GET", "POST"]) -@login_required +@csrf_exempt +@api_view(["GET", "POST", "DELETE"]) +# @login_required def search(request): + user = User.objects.get(id=21) # 임시 유저 지정, 추후 삭제 if request.method == "GET": - histories = SearchHistory.objects.filter(user=request.user) + histories = SearchHistory.objects.filter(user=user) # 추후 삭제 + # histories = SearchHistory.objects.filter(user=request.user) serializer = SearchHistorySerializer(histories, many=True) return Response({"histories": serializer.data}) @@ -32,38 +41,63 @@ def search(request): if not query: return Response({"error": "No search query provided"}, status=400) - SearchHistory.objects.create(user=request.user, query=query) + SearchHistory.objects.create(user=user, query=query) # 추후 삭제 + # SearchHistory.objects.create(user=request.user, query=query) restaurants = Restaurant.objects.filter(name__icontains=query) serializer = RestaurantListSerializer(restaurants, many=True) - return Response({"results": serializer.data}) + data = serializer.data + logging.debug("Serialized data: %s", data) + return Response({"results": data}) + + elif request.method == "DELETE": + history_id = request.data.get("id", "") + if not history_id: + return Response({"error": "No history ID provided"}, status=400) + + try: + history_to_delete = SearchHistory.objects.get(id=history_id, user=user) + history_to_delete.delete() + return Response({"message": "Search history deleted successfully"}) + except SearchHistory.DoesNotExist: + return Response({"error": "No matching search history found"}, status=404) else: return Response({"error": "Unsupported method"}, status=405) +@csrf_exempt @api_view(["GET"]) -@login_required +# @login_required def user_restaurant_list(request): - user_restaurants = UserRestaurantsList.objects.filter(user=request.user) + user = User.objects.get(id=21) # 임시 유저 지정, 추후 삭제 + user_restaurants = UserRestaurantsList.objects.filter(user=user) # 추후 삭제 + # user_restaurants = UserRestaurantsList.objects.filter(user=request.user) serializer = UserRestaurantListSerializer(user_restaurants, many=True) return Response(serializer.data, status=status.HTTP_200_OK) +@csrf_exempt @api_view(["POST", "DELETE"]) -@login_required +# @login_required def add_remove_restaurant(request, pk): + user = User.objects.get(id=21) # 임시 유저 지정, 추후 삭제 try: restaurant = Restaurant.objects.get(pk=pk) if request.method == "POST": - UserRestaurantsList.objects.create(user=request.user, restaurant=restaurant) + UserRestaurantsList.objects.create( + user=user, restaurant=restaurant + ) # 추후 삭제 + # UserRestaurantsList.objects.create(user=request.user, restaurant=restaurant) return Response( {"message": "Restaurant added successfully"}, status=status.HTTP_201_CREATED, ) elif request.method == "DELETE": user_restaurant = UserRestaurantsList.objects.get( - user=request.user, restaurant=restaurant + # user=request.user, restaurant=restaurant + user=user, + restaurant=restaurant, # 추후 삭제 ) user_restaurant.delete() return Response( @@ -81,12 +115,13 @@ def add_remove_restaurant(request, pk): ) +@csrf_exempt @api_view(["GET"]) -@login_required +# @login_required def restaurant_detail(request, pk): try: restaurant = Restaurant.objects.prefetch_related("reviews").get(pk=pk) - serializer = RestaurantSerializer(restaurant) + serializer = RestaurantDetailSerializer(restaurant) return Response(serializer.data) except Restaurant.DoesNotExist: return Response( diff --git a/reviews/models.py b/reviews/models.py index aa5f532..d26c3f1 100644 --- a/reviews/models.py +++ b/reviews/models.py @@ -7,7 +7,9 @@ class Review(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) - restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE) + restaurant = models.ForeignKey( + Restaurant, on_delete=models.CASCADE, related_name="reviews" + ) content = models.CharField(max_length=255) recommend_count = models.IntegerField() decommend_count = models.IntegerField()