Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature admin -> dev 리뷰 응답 데이터 및 문의 권한 수정 #27

Merged
merged 10 commits into from
Sep 3, 2024
Merged
22 changes: 3 additions & 19 deletions customk/questions/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from django.contrib import admin, messages
from django.http import HttpRequest, HttpResponse
from django.utils.html import format_html

from .models import Question

Expand All @@ -11,11 +10,11 @@
class QuestionAdmin(admin.ModelAdmin):
list_display = (
"user_id",
"answer_icon",
"class_id",
"question_title",
"created_at",
"updated_at",
"answer_icon",
)
list_filter = ("class_id", "created_at", "user_id")
search_fields = ("user_id__name", "class_id__title", "question", "question_title")
Expand All @@ -33,11 +32,9 @@ def save_model(self, request, obj, form, change):

def answer_icon(self, obj):
if obj.answer:
return format_html(
'<span style="font-size: 1.2em; color: #00FF00;">✅</span>'
)
return "✅"
else:
return format_html('<span style="font-size: 1.2em; color: red;">❌</span>')
return "❌"

answer_icon.short_description = "답변 상태" # type: ignore

Expand All @@ -53,16 +50,3 @@ def changelist_view(
)

return super().changelist_view(request, extra_context=extra_context)

def change_view(
self,
request: HttpRequest,
object_id: str,
form_url: str = "",
extra_context: Optional[Dict[str, Any]] = None,
) -> HttpResponse:
question = self.get_object(request, object_id)
if question and question.answer is None:
messages.info(request, "이 질문에는 아직 답변이 없습니다.")

return super().change_view(request, object_id, form_url, extra_context)
58 changes: 53 additions & 5 deletions customk/questions/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
inline_serializer,
)
from rest_framework import serializers
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.views import APIView
Expand All @@ -18,28 +19,52 @@


class QuestionListView(APIView):
def get_permissions(self):
if self.request.method == "GET":
return [AllowAny()]
return [IsAuthenticated()]

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

다른 유저가 작성한 질문, 답변들도 볼 수 있을건데 상관 없나요? 기획 쪽에서 들은게 없어서ㅓㅓ 상관없다면 이대로 하시고 자기만 볼 수 있게 하는 기능이 필요하면 user_id 같은걸로 permission 줘야 할 거 같슴니다 !

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

확인했습니다!!

@extend_schema(
methods=["GET"],
summary="질문 및 답변 목록 조회",
description="특정 클래스에 대한 질문 및 답변 목록을 조회하는 API입니다.",
description="특정 클래스에 대한 질문 및 답변 목록을 페이지네이션 형태로 조회하는 API입니다.",
parameters=[
OpenApiParameter(
name="class_id",
description="클래스 ID",
required=True,
type=OpenApiTypes.INT,
location=OpenApiParameter.PATH,
)
),
OpenApiParameter(
name="page",
description="페이지 번호",
required=False,
default=1,
type=OpenApiTypes.INT,
location=OpenApiParameter.QUERY,
),
OpenApiParameter(
name="size",
description="페이지당 항목 수",
required=False,
default=15,
type=OpenApiTypes.INT,
location=OpenApiParameter.QUERY,
),
],
responses={
200: OpenApiResponse(
description="질문 및 답변 목록 조회 성공",
response=inline_serializer(
name="QuestionListResponse",
fields={
"total_count": serializers.IntegerField(),
"total_pages": serializers.IntegerField(),
"current_page": serializers.IntegerField(),
"questions": serializers.ListSerializer(
child=QuestionSerializer()
)
),
},
),
),
Expand All @@ -49,9 +74,32 @@ class QuestionListView(APIView):
def get(
self, request: Request, class_id: int, *args: Any, **kwargs: Any
) -> Response:
questions = Question.objects.filter(class_id=class_id)
page = int(request.query_params.get("page", 1))
size = int(request.query_params.get("size", 15))
offset = (page - 1) * size

if page < 1:
return Response("Page input error", status=400)

try:
questions = Question.objects.filter(class_id=class_id)
except Question.DoesNotExist:
return Response({"message": "Class not found."}, status=404)

total_count = questions.count()
total_pages = (total_count // size) + (1 if total_count % size > 0 else 0)

questions = questions.order_by("-id")[offset : offset + size]

serializer = QuestionSerializer(questions, many=True)
return Response(serializer.data)
response_data = {
"total_count": total_count,
"total_pages": total_pages,
"current_page": page,
"questions": serializer.data,
}

return Response(response_data, status=200)

@extend_schema(
methods=["POST"],
Expand Down
3 changes: 2 additions & 1 deletion customk/reviews/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from classes.models import Class
from common.services.ncp_api_conf import ObjectStorage
from config.logger import logger
from users.serializers.user_serializer import UserSerializer

from .models import Review, ReviewImage

Expand Down Expand Up @@ -63,7 +64,7 @@ def validate(self, data):

class ReviewSerializer(serializers.ModelSerializer):
images = ReviewImageSerializer(many=True, required=False)
user = serializers.ReadOnlyField(source="user.id")
user = UserSerializer()

class Meta:
model = Review
Expand Down
96 changes: 83 additions & 13 deletions customk/reviews/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,22 +32,41 @@ def get_permissions(self):
@extend_schema(
methods=["GET"],
summary="리뷰 목록 조회",
description="특정 클래스에 대한 리뷰 목록을 조회하는 API입니다.",
description="특정 클래스에 대한 리뷰 목록을 페이지네이션 형태로 조회하는 API입니다.",
parameters=[
OpenApiParameter(
name="class_id",
description="클래스 ID",
required=True,
type=OpenApiTypes.INT,
location=OpenApiParameter.PATH,
)
),
OpenApiParameter(
name="page",
description="페이지 번호",
required=False,
default=1,
type=OpenApiTypes.INT,
location=OpenApiParameter.QUERY,
),
OpenApiParameter(
name="size",
description="페이지당 항목 수",
required=False,
default=15,
type=OpenApiTypes.INT,
location=OpenApiParameter.QUERY,
),
],
responses={
200: OpenApiResponse(
description="리뷰 목록 조회 성공",
response=inline_serializer(
name="ReviewListResponse",
fields={
"total_count": serializers.IntegerField(),
"total_pages": serializers.IntegerField(),
"current_page": serializers.IntegerField(),
"reviews": serializers.ListSerializer(
child=inline_serializer(
name="ReviewData",
Expand All @@ -57,7 +76,7 @@ def get_permissions(self):
"dislikes_count": serializers.IntegerField(),
},
)
)
),
},
),
),
Expand All @@ -67,7 +86,18 @@ def get_permissions(self):
def get(self, request: Any, class_id: int, *args: Any, **kwargs: Any) -> Response:
class_instance = get_object_or_404(Class, id=class_id)

page = int(request.GET.get("page", "1"))
size = int(request.GET.get("size", "15"))
offset = (page - 1) * size

if page < 1:
return Response("Page input error", status=400)

reviews = Review.objects.filter(class_id=class_instance.id)
total_count = reviews.count()
total_pages = (total_count // size) + (1 if total_count % size > 0 else 0)

reviews = reviews.order_by("-id")[offset : offset + size]

if not reviews.exists():
return Response({"message": "No reviews found for this class."}, status=404)
Expand All @@ -77,13 +107,17 @@ def get(self, request: Any, class_id: int, *args: Any, **kwargs: Any) -> Respons
reactions = Reaction.get_review_reactions(review)
review_data.append(
{
"review": ReviewSerializer(review).data,
"likes_count": reactions["likes_count"],
"dislikes_count": reactions["dislikes_count"],
"review": {
**ReviewSerializer(review).data,
"likes_count": reactions["likes_count"],
}
}
)

response_data = {
"total_count": total_count,
"total_pages": total_pages,
"current_page": page,
"reviews": review_data,
}
return Response(response_data, status=200)
Expand Down Expand Up @@ -223,8 +257,8 @@ def get_permissions(self):

@extend_schema(
methods=["GET"],
summary="리뷰 이미지 목록 조회",
description="특정 리뷰에 연결된 이미지 목록을 조회하는 API입니다.",
summary="특정 리뷰의 이미지 목록 조회",
description="특정 리뷰에 연결된 이미지 목록을 페이지네이션 형태로 조회하는 API입니다.",
parameters=[
OpenApiParameter(
name="class_id",
Expand All @@ -240,40 +274,76 @@ def get_permissions(self):
type=OpenApiTypes.INT,
location=OpenApiParameter.PATH,
),
OpenApiParameter(
name="page",
description="페이지 번호",
required=False,
default=1,
type=OpenApiTypes.INT,
location=OpenApiParameter.QUERY,
),
OpenApiParameter(
name="size",
description="페이지당 항목 수",
required=False,
default=15,
type=OpenApiTypes.INT,
location=OpenApiParameter.QUERY,
),
],
responses={
200: OpenApiResponse(
description="리뷰 이미지 목록 조회 성공",
response=inline_serializer(
name="ReviewImageListResponse",
name="PhotoReviewListResponse",
fields={
"total_count": serializers.IntegerField(),
"total_pages": serializers.IntegerField(),
"current_page": serializers.IntegerField(),
"images": serializers.ListSerializer(
child=inline_serializer(
name="ReviewImageData",
name="PhotoReviewImageData",
fields={
"id": serializers.IntegerField(),
"image_url": serializers.CharField(),
},
)
)
),
},
),
),
404: OpenApiResponse(description="리뷰 이미지를 찾을 수 없음"),
404: OpenApiResponse(description="이미지를 찾을 수 없음"),
},
)
def get(
self, request: Request, class_id: int, review_id: int, *args: Any, **kwargs: Any
) -> Response:
page = int(request.GET.get("page", "1"))
size = int(request.GET.get("size", "10"))
offset = (page - 1) * size

if page < 1:
return Response("Page input error", status=400)

review_images = ReviewImage.objects.filter(
review_id=review_id, review__class_id=class_id
)
total_count = review_images.count()
total_pages = (total_count // size) + (1 if total_count % size > 0 else 0)

review_images = review_images.order_by("-id")[offset : offset + size]

if not review_images.exists():
return Response({"message": "No images found for this review."}, status=404)

serializer = ReviewImageSerializer(review_images, many=True)
return Response({"images": serializer.data}, status=200)
response_data = {
"total_count": total_count,
"total_pages": total_pages,
"current_page": page,
"images": serializer.data,
}
return Response(response_data, status=200)


class PhotoReviewListView(generics.ListAPIView):
Expand Down