카테고리 없음

내일 배움 캠프 최종 프로젝트

하늘유니콘 2023. 7. 2. 17:31

📚 stacks

 

 

 

안녕하세요, 반갑습니다! ConnectME 서비스를 만든 사회화지원소 입니다.
저희가 만든 서비스에 대한 사용자 피드백을 받아보고자 이렇게 인사를 드리게

되었습니다.

 

   서비스 소개

 

ConnectME는 친구 만들기 서비스입니다. 새 친구를 사귀고 싶거나, 맛집에 가고 취미생활을 하고 싶은데 같이 갈 사람이 없는 분들을 위해 만들게 되었습니다.
가깝거나 성격이 비슷한 친구들을 소개받고, 1:1 채팅을 통해 소통할 수 있습니다. 또한 모일 장소를 추천받아 모임을 생성할 수 있고, 익명으로 고민상담도 할 수 있는 서비스입니다.

안녕하세요, 반갑습니다! ConnectME 서비스를 만든 사회화지원소 입니다.
저희가 만든 서비스에 대한 사용자 피드백을 받아보고자 이렇게 인사를 드리게

되었습니다.

 

   서비스 소개

 

ConnectME는 친구 만들기 서비스입니다. 새 친구를 사귀고 싶거나, 맛집에 가고 취미생활을 하고 싶은데 같이 갈 사람이 없는 분들을 위해 만들게 되었습니다.
가깝거나 성격이 비슷한 친구들을 소개받고, 1:1 채팅을 통해 소통할 수 있습니다. 또한 모일 장소를 추천받아 모임을 생성할 수 있고, 익명으로 고민상담도 할 수 있는 서비스입니다.

안녕하세요, 반갑습니다! ConnectME 서비스를 만든 사회화지원소 입니다.
저희가 만든 서비스에 대한 사용자 피드백을 받아보고자 이렇게 인사를 드리게

되었습니다.

 

   서비스 소개

 

ConnectME는 친구 만들기 서비스입니다. 새 친구를 사귀고 싶거나, 맛집에 가고 취미생활을 하고 싶은데 같이 갈 사람이 없는 분들을 위해 만들게 되었습니다.
가깝거나 성격이 비슷한 친구들을 소개받고, 1:1 채팅을 통해 소통할 수 있습니다. 또한 모일 장소를 추천받아 모임을 생성할 수 있고, 익명으로 고민상담도 할 수 있는 서비스입니다.

 

 

친구찿기 메인페인지 입니다.

프로필 메뉴 버튼이 있는데요 메뉴 클릭하면 프로필 정보를 수정이 가능합니다.

 

리스트 형식으로 고민상담거리를 게시판처럼 이용 할 수 있습니다.

 

models.py

from django.db import models
from user.models import User

"""상담 게시글 모델입니다."""
class Counsel(models.Model):
    user = models.ForeignKey(User, verbose_name="작성자", on_delete=models.CASCADE)
    title = models.CharField(max_length=50, verbose_name="제목")
    content = models.TextField(verbose_name="내용")
    like = models.ManyToManyField(User, related_name='counsel_like', verbose_name="좋아요")
    created_at = models.DateTimeField(verbose_name="작성일" , auto_now_add=True,)
    updated_at = models.DateTimeField(verbose_name="수정일" , auto_now=True)

"""상담 댓글 모델입니다."""    
class CounselComment(models.Model):
    user = models.ForeignKey(User, verbose_name="댓글작성자", on_delete=models.CASCADE, related_name="counsel_comment_user")
    counsel= models.ForeignKey(Counsel, on_delete=models.CASCADE, related_name="counsel_comment_counsel")
    content = models.TextField(verbose_name="내용")
    like = models.ManyToManyField(User, related_name='counsel_comment_like', verbose_name="좋아요")
    created_at = models.DateTimeField(verbose_name="작성일" , auto_now_add=True,)
    updated_at = models.DateTimeField(verbose_name="수정일" , auto_now=True)

"""상담 답글 모델입니다."""    
class CounselReply(models.Model):
    user = models.ForeignKey(User, verbose_name="댓글작성자", on_delete=models.CASCADE, related_name="counsel_reply_user")
    counsel= models.ForeignKey(Counsel, on_delete=models.CASCADE)
    comment = models.ForeignKey(CounselComment, verbose_name="댓글", on_delete=models.CASCADE, related_name="reply")
    content = models.TextField(verbose_name="내용")
    like = models.ManyToManyField(User, related_name='counsel_reply_like', verbose_name="좋아요")
    created_at = models.DateTimeField(verbose_name="작성일" , auto_now_add=True,)
    updated_at = models.DateTimeField(verbose_name="수정일" , auto_now=True)
Counsel: 상담 게시글 모델입니다.



모델기능을 간단하게 요약을 해봅니다.

user: 작성자를 나타내는 외래키 입니다.

.

title: 상담 게시글의 제목을 알려줍니다.



content: 상담 게시글의 내용 입니다.



like: 사용자들이 좋아요 누르는 갯수만큼 나옵니다.



created_at:게시글의 작성일을 알려주는 기능입니다.



update_at:게시글의 수정일을 알려주는 기능입니다.



CounselComment: 상담 게시글에 대한 댓글을 나타내는 모델입니다. 



user: 댓글을 나타내는 외래 키 입니다.



counsel:댓글에 속한 상담 게시글을 나타내는 외래 키 입니다.



content: 댓글 내용을 담는 텍스트입니다.



like: 사용자들이 좋아요 누르는 갯수만큼 나옵니다.



created_at:댓글의 작성일을 알려주는 기능입니다.



update_at:댓글의 수정일을 알려주는 기능입니다.



CounselReply: 상담 댓글에 대한 모델입니다.



user: 답글 작성자를 나타내는 외래 키 입니다.



counsel:답글이 속한 상담 게시글을 나타내는 외래 키 입니다.



content: 답글 내용을 담는 텍스트입니다.



like: 사용자들이 좋아요 누르는 갯수만큼 나옵니다.



created_at:답글의 작성일을 알려주는 기능입니다.



update_at:답글의 수정일을 알려주는 기능입니다.

from rest_framework import serializers
from .models import (
    Counsel,
    CounselComment,
    CounselReply
)

""" 대댓글 """

'''대댓글 작성'''
class CounselReplyCreateSerializer(serializers.ModelSerializer):
    class Meta:
        model = CounselReply
        fields = ("content",)
        extra_kwargs = {
            "content": {
                "error_messages": {
                    "required": "댓글을 입력해주세요.",
                    "blank": "댓글을 입력해주세요.",
                }
            },
        }

'''대댓글'''
class CounselReplySerializer(serializers.ModelSerializer):
    user = serializers.SerializerMethodField()
    reply_like_count = serializers.SerializerMethodField()
    comment_created_at = serializers.DateTimeField(
        format="%y-%m-%d %H:%M", read_only=True
    )
    def get_reply_like_count(self, obj):
        return obj.like.count()

    def get_user(self, obj):
        return {"nickname": obj.user.nickname, "pk": obj.user.pk}

    class Meta:
        model = CounselReply
        fields = "__all__"

""" 댓글 """

class CounselCommentSerializer(serializers.ModelSerializer):
    reply = CounselReplySerializer(many=True)
    user = serializers.SerializerMethodField()
    comment_like_count = serializers.SerializerMethodField()
    comment_created_at = serializers.DateTimeField(
        format="%y-%m-%d %H:%M", read_only=True
    )

    def get_user(self, obj):
        return {"account": obj.user.account, "pk": obj.user.pk, "nickname": obj.user.nickname}
    
    def get_comment_like_count(self, obj):
        return obj.like.count()

    class Meta:
        model = CounselComment
        fields = "__all__"

'''댓글 작성'''
class CounselCommentCreateSerializer(serializers.ModelSerializer):
    class Meta:
        model = CounselComment
        fields = ("content",)
        extra_kwargs = {
            "content": {
                "error_messages": {
                    "required": "댓글을 입력해주세요.",
                    "blank": "댓글을 입력해주세요.",
                }
            },
        }


""" 글 작성, 상세, 수정 """

'''글 리스트'''
class CounselListSerializer(serializers.ModelSerializer):
    user = serializers.SerializerMethodField()
    created_at = serializers.DateTimeField(format="%Y년 %m월 %d일 %H시 %M분")
    
    def get_user(self, obj):
        return {"account": obj.user.account, "pk": obj.user.pk, "nickname": obj.user.nickname}

    class Meta:
        model = Counsel
        fields = "__all__"

        

'''글 작성, 수정'''
class CounselCreateSerializer(serializers.ModelSerializer):

    class Meta:
        model = Counsel
        exclude = ['user', 'like', 'created_at', 'updated_at']
        extra_kwargs={
            "title": {
                "error_messages": {
                    "blank": "제목을 입력해주세요",
                }
            },
            "content": {
                "error_messages": {
                    "blank": "내용을 입력해주세요",
                },
            },
        }

'''글 상세'''
class CounselDetailSerializer(serializers.ModelSerializer):
    created_at = serializers.DateTimeField(format="%Y년 %m월 %d일 %H시 %M분")
    updated_at = serializers.DateTimeField(format="%Y년 %m월 %d일 %H시 %M분")
    user = serializers.SerializerMethodField()
    counsel_comment_counsel = CounselCommentSerializer
    def get_user(self, obj):
        return {"account": obj.user.account, "pk": obj.user.pk, "nickname": obj.user.nickname}
    
    class Meta:
        model = Counsel
        fields = "__all__"

 

CounselReplyCreateSerializer: 대댓글 작성을 위한 Serializer 클래스입니다. CounselReply 모델과 연결되어 있으며, content 필드를 입력 받습니다.

CounselReplySerializer: 대댓글 정보를 나타내기 위한 Serializer 클래스입니다. CounselReply 모델과 연결되어 있으며, user, reply_like_count, comment_created_at 필드를 반환합니다. get_reply_like_count 메서드는 대댓글의 좋아요 수를 계산하고, get_user 메서드는 대댓글 작성자의 닉네임과 pk를 반환합니다.

CounselCommentSerializer: 댓글 정보를 나타내기 위한 Serializer 클래스입니다. CounselComment 모델과 연결되어 있으며, reply, user, comment_like_count, comment_created_at 필드를 반환합니다. reply 필드는 CounselReplySerializer를 사용하여 해당 댓글의 대댓글을 시리얼라이즈합니다. get_user 메서드는 댓글 작성자의 계정, pk, 닉네임을 반환하고, get_comment_like_count 메서드는 댓글의 좋아요 수를 계산합니다.

CounselCommentCreateSerializer: 댓글 작성을 위한 Serializer 클래스입니다. CounselComment 모델과 연결되어 있으며, content 필드를 입력 받습니다.

CounselListSerializer: 상담 게시글 목록을 나타내기 위한 Serializer 클래스입니다. Counsel 모델과 연결되어 있으며, user, created_at 필드를 반환합니다. get_user 메서드는 게시글 작성자의 계정, pk, 닉네임을 반환합니다.

CounselCreateSerializer: 상담 게시글 작성 및 수정을 위한 Serializer 클래스입니다. Counsel 모델과 연결되어 있으며, title, content를 입력 받습니다. 일부 필드는 제외되고, 추가적인 유효성 검사 메시지가 설정되어 있습니다.

CounselDetailSerializer: 상담 게시글 상세 정보를 나타내기 위한 Serializer 클래스입니다. Counsel 모델과 연결되어 있으며, 모든 필드를 반환합니다. created_at, updated_at 필드는 지정된 형식으로 변환되고, user 필드는 게시글 작성자의 계정, pk, 닉네임을 반환합니다. counsel_comment_counsel 필드는 CounselCommentSerializer를 사용하여 해당 게시글의 댓글을 시리얼라이즈합니다.

 

from rest_framework.views import APIView
from rest_framework.generics import get_object_or_404
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated,AllowAny
from rest_framework.pagination import PageNumberPagination

from .models import(
    Counsel,
    CounselComment,
    CounselReply,
    )


from .serializers import(
    CounselListSerializer,
    CounselCreateSerializer,
    CounselDetailSerializer,
    CounselCommentSerializer,
    CounselCommentCreateSerializer,
    CounselReplySerializer,
    CounselReplyCreateSerializer,
)

'''페이지네이션 시작'''
class CounselPagination(PageNumberPagination):
    page_size = 15
    
'''페이지네이션 끝'''
'''게시글 시작'''

class CounselView(APIView):
    permission_classes = [AllowAny]
    pagination_class = CounselPagination()
    
    def get_permissions(self):
        if self.request.method == "POST":
            return [IsAuthenticated(),]
        else:
            return super(CounselView, self).get_permissions()
        
    '''글목록'''
    def get(self, request):
        counsels = Counsel.objects.all().order_by('-id')
        paginator = self.pagination_class
        result_page = paginator.paginate_queryset(counsels, request)
        total_items = paginator.page.paginator.count
        serializer = CounselListSerializer(result_page, many=True)
        return Response({"counsel": serializer.data, "total-page": total_items}, status=status.HTTP_200_OK)

    '''글작성'''
    def post(self, request):
        serializer = CounselCreateSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save(user=request.user)
            return Response(serializer.data, status=status.HTTP_200_OK)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)



class CounselDetailView(APIView):
    
    permission_classes = [AllowAny]
    
    def get_permissions(self):
        if self.request.method == "PUT" or self.request.method == "DELETE":
            return [IsAuthenticated(),]
        else:
            return super(CounselDetailView, self).get_permissions()
        
    '''글상세'''
    def get(self, request, counsel_id):
        counsel = get_object_or_404(Counsel, id=counsel_id)
        counsel_serializer = CounselDetailSerializer(counsel)
        return Response({'counsel':counsel_serializer.data}, status=status.HTTP_200_OK)
    
    '''글수정'''
    def put(self, request, counsel_id):
        counsel = get_object_or_404(Counsel, id=counsel_id)
        if counsel.user == request.user:
            serializer = CounselCreateSerializer(counsel, data=request.data, partial=True)
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data, status.HTTP_200_OK)
            else:
                return Response(serializer.errors, status.HTTP_400_BAD_REQUEST)
        else:
            return Response({"message":"권한이 없습니다."}, status.HTTP_403_FORBIDDEN)
    '''글삭제'''
    def delete(self, request, counsel_id):
        counsel = get_object_or_404(Counsel, id=counsel_id)
        if counsel.user == request.user:
            counsel.delete()
            return Response({'message': '삭제 완료'},status=status.HTTP_200_OK)
        else:
            return Response({"message":"권한이 없습니다."}, status.HTTP_403_FORBIDDEN)
        
'''작성한 게시글 모아보기'''
class MyCreateCounselView(APIView):
    permission_classes = [IsAuthenticated]
    pagination_class = CounselPagination
    
    def get(self, request):
        counsel = Counsel.objects.filter(user=request.user).order_by('-id')
        paginator = self.pagination_class()
        result_page = paginator.paginate_queryset(counsel, request)
        total_items = paginator.page.paginator.count
        serializer = CounselListSerializer(result_page, many=True)
        return Response({"counsel": serializer.data, "total-page": total_items}, status.HTTP_200_OK)
            
'''게시글 좋아요'''
class CounselLikeView(APIView):
    permission_classes = [IsAuthenticated]
    
    def post(self, request, counsel_id):
        counsel = get_object_or_404(Counsel, id=counsel_id)
        if request.user in counsel.like.all():
            counsel.like.remove(request.user)
            counsel_like = counsel.like.count()
            return Response({"message":"좋아요 취소", "counsel_like":counsel_like}, status=status.HTTP_200_OK)
        else:
            counsel.like.add(request.user)
            counsel_like = counsel.like.count()
            return Response({"message":"좋아요", "counsel_like":counsel_like}, status=status.HTTP_202_ACCEPTED)

""" 게시글 끝 """

""" 댓글 시작 """

class CounselCommentView(APIView):
    permission_classes = [AllowAny]
    
    def get_permissions(self):
        if self.request.method == "PUT" or self.request.method == "DELETE":
            return [IsAuthenticated(),]
        elif self.request.method == "POST":
            return [IsAuthenticated(),]
        else:
            return super(CounselCommentView, self).get_permissions()
        
    
    '''댓글리스트'''
    def get(self, request, counsel_id):
        counsel = get_object_or_404(Counsel, id=counsel_id)
        comments = counsel.counsel_comment_counsel.all()
        serializer = CounselCommentSerializer(comments, many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)
    
    '''댓글작성'''
    def post(self, request, counsel_id):
        serializer = CounselCommentCreateSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save(user=request.user, counsel_id=counsel_id)
            return Response({"meesage":"작성완료"}, status=status.HTTP_201_CREATED)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        

class CounselCommentDetailView(APIView):
    '''댓글수정'''
    def put(self, request, counsel_id, counsel_comment_id):
        comment = get_object_or_404(CounselComment, id=counsel_comment_id)
        if comment.user == request.user:
            serializer = CounselCreateSerializer(comment, data=request.data, partial=True)
            if serializer.is_valid():
                serializer.save()
                return Response({"meesage":"수정완료"}, status.HTTP_200_OK)
            else:
                return Response(serializer.errors, status.HTTP_400_BAD_REQUEST)
        else:
            return Response({"message":"권한이 없습니다."}, status.HTTP_403_FORBIDDEN)

    '''댓글삭제'''
    def delete(self, request, counsel_id, counsel_comment_id):
        comment = get_object_or_404(CounselComment, id=counsel_comment_id)
        if request.user == comment.user:
            if comment.reply.all():
                serializer = CounselReplyCreateSerializer(comment, {"content":"삭제된 댓글 입니다."} )
                if serializer.is_valid():
                    serializer.save(content="삭제된 댓글 입니다.")
                    return Response(serializer.data, status=status.HTTP_204_NO_CONTENT)
                else:
                    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
            else:
                comment.delete()
                return Response({'message': '삭제 완료'},status=status.HTTP_200_OK)
        else:
            return Response({"message":"권한이 없습니다."}, status=status.HTTP_403_FORBIDDEN)


class CounselCommentLikelView(APIView):
    '''댓글 좋아요'''
    def post(self, request, counsel_id, counsel_comment_id):
        counselcomment = get_object_or_404(CounselComment, id=counsel_comment_id)
        if request.user in counselcomment.like.all():
            counselcomment.like.remove(request.user)
            comment_like = counselcomment.like.count()
            return Response({"message": "댓글 좋아요 취소", "comment_like":comment_like}, status=status.HTTP_200_OK)
        else:
            counselcomment.like.add(request.user)
            comment_like = counselcomment.like.count()
            return Response({"message": "댓글 좋아요", "comment_like":comment_like}, status=status.HTTP_202_ACCEPTED)
        
""" 댓글 끝 """


""" 대댓글 시작 """

class CounselReplyView(APIView):
    permission_classes = [AllowAny]
    
    def get_permissions(self):
        if self.request.method == "PUT" or self.request.method == "DELETE":
            return [IsAuthenticated(),]
        elif self.request.method == "POST":
            return [IsAuthenticated(),]
        else:
            return super(CounselReplyView, self).get_permissions()
        
    '''대댓글 리스트'''
    def get(self, request, counsel_id, counsel_comment_id):
        reply = CounselReply.objects.all()
        serializer = CounselReplySerializer(reply, many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)

    '''대댓글 작성'''
    def post(self, request, counsel_id, counsel_comment_id):
        serializer = CounselReplyCreateSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save(user=request.user, counsel_id=counsel_id, comment_id=counsel_comment_id)
            return Response(serializer.data, status=status.HTTP_200_OK)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        

class CounselReplyDetailView(APIView):
    '''대댓글 수정'''
    def put(self, request, counsel_id, counsel_reply_id):
        reply = get_object_or_404(CounselReply, id=counsel_reply_id)
        if request.user == reply.user:
            serializer = CounselReplyCreateSerializer(reply, request.data)
            if serializer.is_valid():
                serializer.save()
                return Response({"message":"성공."}, status=status.HTTP_200_OK)
            else:
                return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        else:
            return Response({"message":"권한이 없습니다."}, status=status.HTTP_403_FORBIDDEN)
            
    '''대댓글 삭제'''
    def delete(self, request, counsel_id, counsel_reply_id):
        reply = get_object_or_404(CounselReply, id=counsel_reply_id)
        if request.user == reply.user:
            reply.delete()
            return Response({"message":"삭제완료"},status=status.HTTP_200_OK)
        else:
            return Response({"message":"권한이 없습니다."}, status=status.HTTP_403_FORBIDDEN)
    

class CounselReplyLikeView(APIView):
    permission_classes = [IsAuthenticated]
    '''대댓글 좋아요'''
    def post(self, request, counsel_id, counsel_reply_id):
        counselreply = get_object_or_404(CounselReply, id=counsel_reply_id)
        if request.user in counselreply.like.all():
            counselreply.like.remove(request.user)
            reply_like = counselreply.like.count()
            return Response({"message": "좋아요 취소", "reply_like":reply_like}, status=status.HTTP_200_OK)
        else:
            counselreply.like.add(request.user)
            reply_like = counselreply.like.count()
            return Response({"message": "좋아요", "reply_like":reply_like}, status=status.HTTP_202_ACCEPTED)

""" 대댓글 끝 """
CounselView: 게시글 목록을 조회하고, 새로운 게시글을 작성합니다.
CounselDetailView: 특정 게시글의 상세 정보를 조회하고, 게시글을 수정하거나 삭제합니다.
MyCreateCounselView: 특정 사용자가 작성한 게시글을 조회합니다.
CounselLikeView: 게시글에 좋아요를 추가하거나 취소합니다.
CounselCommentView: 특정 게시글에 대한 댓글 목록을 조회하고, 새로운 댓글을 작성합니다.
CounselCommentDetailView: 특정 댓글을 수정하거나 삭제합니다. 댓글에 대한 대댓글이 있는 경우 대댓글 내용을 수정하여 댓글을 삭제한 것처럼 처리합니다.
CounselCommentLikelView: 댓글에 좋아요를 추가하거나 취소합니다.
CounselReplyView: 특정 댓글에 대한 대댓글 목록을 조회하고, 새로운 대댓글을 작성합니다.
CounselReplyDetailView: 특정 대댓글을 수정하거나 삭제합니다.
CounselReplyLikeView: 대댓글에 좋아요를 추가하거나 취소합니다.

만남의 광장 카드 리스트입니다.

 

간단하게 핫플렉스 보겠습니다.

 

 

친구도 만나고 지역 정보들에 맛집 정보들을 알 수 있게 정보를 만들었습니다.

크롤링으로 작업을 하고 그 데이터에 맞게 프런트를 입혔습니다.

 

 

슬라이드 형식으로 부드럽게 슬라이드해서 사진을 볼 수 있습니다 몰론 프런트지만