From 9f046073c56434a7def2559b6bcc55ab640fcb3b Mon Sep 17 00:00:00 2001 From: sm4640 Date: Fri, 2 May 2025 22:07:09 +0900 Subject: [PATCH 01/10] =?UTF-8?q?=E2=9C=A8=20Feat:=20[#41]=20notification?= =?UTF-8?q?=20=EC=95=B1=20=EC=83=9D=EC=84=B1=20=EB=B0=8F=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- notifications/__init__.py | 0 notifications/admin.py | 3 +++ notifications/apps.py | 6 ++++++ notifications/models.py | 11 +++++++++++ notifications/serializers.py | 32 ++++++++++++++++++++++++++++++++ notifications/services.py | 19 +++++++++++++++++++ notifications/tests.py | 3 +++ notifications/urls.py | 14 ++++++++++++++ notifications/views.py | 34 ++++++++++++++++++++++++++++++++++ 9 files changed, 122 insertions(+) create mode 100644 notifications/__init__.py create mode 100644 notifications/admin.py create mode 100644 notifications/apps.py create mode 100644 notifications/models.py create mode 100644 notifications/serializers.py create mode 100644 notifications/services.py create mode 100644 notifications/tests.py create mode 100644 notifications/urls.py create mode 100644 notifications/views.py diff --git a/notifications/__init__.py b/notifications/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/notifications/admin.py b/notifications/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/notifications/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/notifications/apps.py b/notifications/apps.py new file mode 100644 index 0000000..001b4f9 --- /dev/null +++ b/notifications/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class NotificationsConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'notifications' diff --git a/notifications/models.py b/notifications/models.py new file mode 100644 index 0000000..e426773 --- /dev/null +++ b/notifications/models.py @@ -0,0 +1,11 @@ +from django.db import models + +from users.models import * + +from common.models.baseModels import * + +class Notification(BaseModel): + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='notifications') + content = models.TextField(blank=True) + is_read = models.BooleanField(default=False) + note_type = models.CharField(max_length=10, choices=NotificationType.choices) \ No newline at end of file diff --git a/notifications/serializers.py b/notifications/serializers.py new file mode 100644 index 0000000..4bbd23c --- /dev/null +++ b/notifications/serializers.py @@ -0,0 +1,32 @@ +from .models import * +from projects.models import * +from rest_framework import serializers + +class NotificationSerializer(serializers.ModelSerializer): + meta = serializers.SerializerMethodField() + + class Meta: + model = Notification + fields = ['id', 'content', 'note_type', 'is_read', 'meta'] + + def get_meta(self, obj): + + REL_SERIALIZER_MAP = { + 'project_invitation' : ProjectInvitationMetaSerializer, + } + + for rel_name, serializer_cls in REL_SERIALIZER_MAP.items(): + rel_obj = getattr(obj, rel_name, None) + if rel_obj is not None: + return serializer_cls(rel_obj).data + return None + +class ProjectInvitationMetaSerializer(serializers.ModelSerializer): + project_invitation_id = serializers.CharField(source='id') + project_title = serializers.CharField(source='project.title') + from_user_nickname = serializers.CharField(source='from_user.nickname') + + class Meta: + model = ProjectInvitation + fields = ['project_invitation_id', 'project_title', 'from_user_nickname', 'status'] + \ No newline at end of file diff --git a/notifications/services.py b/notifications/services.py new file mode 100644 index 0000000..ff353af --- /dev/null +++ b/notifications/services.py @@ -0,0 +1,19 @@ +from users.models import * +from .models import * + +from common.models.choiceModels import * + + + +# 알림 관련 서비스 로직 +class NotifiationService: + @staticmethod + def set_content(): + pass + + @staticmethod + def create_notification(user: User, note_type: NotificationType): + return Notification.objects.create( + user = user, + note_type=note_type + ) \ No newline at end of file diff --git a/notifications/tests.py b/notifications/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/notifications/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/notifications/urls.py b/notifications/urls.py new file mode 100644 index 0000000..c2d898b --- /dev/null +++ b/notifications/urls.py @@ -0,0 +1,14 @@ +from django.urls import path, include + +from .views import * + +from rest_framework.routers import DefaultRouter + +app_name = 'notifications' + +router = DefaultRouter() +router.register(r'', NotificationReadViewSet, basename='notification') + +urlpatterns = [ + path('', include(router.urls)), +] \ No newline at end of file diff --git a/notifications/views.py b/notifications/views.py new file mode 100644 index 0000000..aae2dfb --- /dev/null +++ b/notifications/views.py @@ -0,0 +1,34 @@ +from django.shortcuts import get_object_or_404 +from django.db import transaction + +from rest_framework.viewsets import ReadOnlyModelViewSet +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework import status + +from .serializers import * +from .models import * +from .services import * + +from users.services import * + + +# Create your views here. +class NotificationReadViewSet(ReadOnlyModelViewSet): + serializer_class = NotificationSerializer + + # 30일 이전 알림만 가져옴 + def get_queryset(self): + qs = UserToNotificationService.get_all_notification(self.request.user) + print(qs) + return qs.select_related( + 'project_invitation__project', + 'project_invitation__from_user', + ) + + def retrieve(self, request, *args, **kwargs): + instance = self.get_object() + instance.is_read = True + instance.save() + serializer = self.get_serializer(instance) + return Response(serializer.data, status=status.HTTP_200_OK) \ No newline at end of file From d6bcd52d44e1237ecb0d8710c128c1fae1b7231b Mon Sep 17 00:00:00 2001 From: sm4640 Date: Fri, 2 May 2025 22:08:29 +0900 Subject: [PATCH 02/10] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix:=20[#41]=20lists?= =?UTF-8?q?erializer=EC=97=90=20title=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- portfolios/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/portfolios/serializers.py b/portfolios/serializers.py index 35948f8..6d8e8bc 100644 --- a/portfolios/serializers.py +++ b/portfolios/serializers.py @@ -8,7 +8,7 @@ class PortfolioListViewSerializer(serializers.ModelSerializer): class Meta: model = Portfolio - fields = ['id', 'category', 'thumbnail', 'nickname', 'profile_image', 'view_count', 'like_count', 'scrap_count'] + fields = ['id', 'category', 'thumbnail', 'title', 'nickname', 'profile_image', 'view_count', 'like_count', 'scrap_count'] class PortfolioCreateSerializer(serializers.ModelSerializer): class Meta: From 23c6a1d22d69948148dd3e7b16ad81f287b80bac Mon Sep 17 00:00:00 2001 From: sm4640 Date: Fri, 2 May 2025 22:09:40 +0900 Subject: [PATCH 03/10] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix:=20[#41]=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=EC=A0=9D=ED=8A=B8=20=EC=B4=88=EB=8C=80=20?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=EC=97=90=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EC=97=B0=EA=B4=80=EA=B4=80=EA=B3=84=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- projects/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/projects/models.py b/projects/models.py index 88ed65d..d034525 100644 --- a/projects/models.py +++ b/projects/models.py @@ -6,6 +6,7 @@ from common.models.choiceModels import InvitationStatus from django.contrib.postgres.fields import ArrayField from django.conf import settings +from notifications.models import Notification from users.models import User @@ -35,3 +36,4 @@ class ProjectInvitation(BaseModel): to_user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='received_invitations') project = models.ForeignKey(Project, on_delete=models.CASCADE) status = models.CharField(max_length=10, choices=InvitationStatus.choices) + notification = models.OneToOneField(Notification, on_delete=models.CASCADE, related_name='project_invitation', null=True, blank=True) \ No newline at end of file From df1a861597d116dd919539af6c7a1e8141cb40c4 Mon Sep 17 00:00:00 2001 From: sm4640 Date: Fri, 2 May 2025 22:11:06 +0900 Subject: [PATCH 04/10] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix:=20[#41]=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=EC=A0=9D=ED=8A=B8=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EC=8B=9C=20=EB=B0=94=EB=A1=9C=20=EC=B4=88=EB=8C=80=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EA=B3=A0=20=EC=B4=88=EB=8C=80/=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=ED=85=8C=EC=9D=B4=EB=B8=94=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- projects/serializers.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/projects/serializers.py b/projects/serializers.py index a8d3af0..a5f8902 100644 --- a/projects/serializers.py +++ b/projects/serializers.py @@ -1,8 +1,12 @@ from .models import * +from .services import * from users.models import User from rest_framework import serializers +from notifications.services import * + +from common.models.choiceModels import * class ProjectListViewSerializer(serializers.ModelSerializer): class Meta: @@ -32,8 +36,13 @@ class ProjectCreateSerializer(serializers.ModelSerializer): users = list(users) + [validated_data["owner"]] for user in users: - ProjectTeamList.objects.create(user=user, project=project) - + new_notification = NotifiationService.create_notification(user=user, note_type=NotificationType.INVITE) + ProjectInvitationService.create_project_invitation( + project=project, + from_user=validated_data['owner'], + to_user=user, + notification=new_notification + ) return project class ProjectTeamSerializer(serializers.ModelSerializer): From 973dbc85b67d52f4f61280ef87d7e3408a395e29 Mon Sep 17 00:00:00 2001 From: sm4640 Date: Fri, 2 May 2025 22:11:50 +0900 Subject: [PATCH 05/10] =?UTF-8?q?=E2=9C=A8=20Feat:=20[#41]=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=EC=A0=9D=ED=8A=B8=20=EC=B4=88=EB=8C=80=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=20=EA=B0=9D=EC=B2=B4=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EC=84=9C=EB=B9=84=EC=8A=A4=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- projects/services.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/projects/services.py b/projects/services.py index 50b9a64..e993e06 100644 --- a/projects/services.py +++ b/projects/services.py @@ -1,4 +1,8 @@ -from .models import Project +from .models import * +from .serializers import * + +from users.models import * +from common.models.choiceModels import InvitationStatus ACTION_FIELD_MAP = { @@ -38,3 +42,16 @@ class ProjectStateChangeService: now_count = current_count+1 if add else max(current_count-1, 0) setattr(project, field_name, now_count) project.save(update_fields=[field_name]) + + +class ProjectInvitationService: + @staticmethod + def create_project_invitation(project: Project, from_user: User, to_user: User, notification: Notification): + return ProjectInvitation.objects.create( + project=project, + from_user=from_user, + to_user=to_user, + status= InvitationStatus.PENDING, + notification=notification + ) + \ No newline at end of file From 329dac7aeabb27c1580cf472de17e7a46bd67f60 Mon Sep 17 00:00:00 2001 From: sm4640 Date: Fri, 2 May 2025 22:12:25 +0900 Subject: [PATCH 06/10] =?UTF-8?q?=E2=9C=A8=20Feat:=20[#41]=20=EC=B4=88?= =?UTF-8?q?=EB=8C=80=20=EC=88=98=EB=9D=BD/=EA=B1=B0=EC=A0=88=20url=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- projects/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/urls.py b/projects/urls.py index 47b7f66..df727f8 100644 --- a/projects/urls.py +++ b/projects/urls.py @@ -12,5 +12,5 @@ urlpatterns = [ path('/set-represent/', ProjectSetRepresentAPIView.as_view()), path('/set-publish/', ProjectSetPublishAPIView.as_view()), path('/change-state/', ProjectChangeState.as_view()), - + path('invite/action/', ProjectInvitationAPIView.as_view()), ] \ No newline at end of file From 7e31c799632af2a9a788b51998832b067396da35 Mon Sep 17 00:00:00 2001 From: sm4640 Date: Fri, 2 May 2025 22:13:07 +0900 Subject: [PATCH 07/10] =?UTF-8?q?=E2=9C=A8=20Feat:=20[#41]=20=ED=8C=80?= =?UTF-8?q?=EC=9B=90=20=EC=B4=88=EB=8C=80=20=EC=88=98=EB=9D=BD/=EA=B1=B0?= =?UTF-8?q?=EC=A0=88=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- projects/views.py | 45 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/projects/views.py b/projects/views.py index 64c8da8..6409d85 100644 --- a/projects/views.py +++ b/projects/views.py @@ -12,10 +12,12 @@ from rest_framework.generics import ListAPIView from .serializers import ProjectListViewSerializer, ProjectCreateSerializer, ProjectTeamSerializer from .paginations import ProjectPagination from .filters import ProjectFilter -from .services import ProjectStateChangeService, ProjectBeforeRelCheckService +from .services import * from django.db import transaction +from notifications.services import * + class ProjectListView(ListAPIView): queryset = Project.objects.filter(is_published=True).order_by('-created_at') @@ -29,6 +31,7 @@ class ProjectListView(ListAPIView): class ProjectCreateAPIView(APIView): @transaction.atomic def post(self, request): + user = request.user serializer = ProjectCreateSerializer(data=request.data, context={"request": request}) if serializer.is_valid(): serializer.save() @@ -36,7 +39,7 @@ class ProjectCreateAPIView(APIView): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) class ProjectTeamManageAPIView(APIView): - # 팀원 초대 + # 팀원 초대 및 알림 @transaction.atomic def post(self, request, pk): user = request.user @@ -47,11 +50,14 @@ class ProjectTeamManageAPIView(APIView): new_member = get_object_or_404(User, nickname=nickname) if ProjectTeamList.objects.filter(project=project, user=new_member).exists(): return Response({"message": "already team member"}, status=status.HTTP_400_BAD_REQUEST) - serializer = ProjectTeamSerializer(data={'user':new_member.id, 'project':project.id}) - if serializer.is_valid(): - serializer.save() - return Response({"message": "invite success"}, status=status.HTTP_200_OK) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + new_notification = NotifiationService.create_notification(user=new_member, note_type=NotificationType.INVITE) + ProjectInvitationService.create_project_invitation( + project=project, + from_user=user, + to_user=new_member, + notification=new_notification + ) + return Response({"message": "invite success"}, status=status.HTTP_200_OK) # 팀원 추방 @transaction.atomic @@ -140,4 +146,27 @@ class ProjectChangeState(APIView): return Response({ 'message': f'{action_type} {"added" if add else "removed"}' }, status=status.HTTP_200_OK) - + +class ProjectInvitationAPIView(APIView): + # 팀원 초대 수락/거절 + @transaction.atomic + def post(self, request): + accept = request.data['accept'] + project_invitation = get_object_or_404(ProjectInvitation, id=request.data['project_invitation_id']) + new_member = project_invitation.to_user + project = project_invitation.project + if request.user != new_member: + return Response({"message": "Not account owner"}, status=status.HTTP_403_FORBIDDEN) + if project_invitation.status != InvitationStatus.PENDING: + return Response({"message": "already handled invitation"}, status=status.HTTP_400_BAD_REQUEST) + if accept: + project_invitation.status = InvitationStatus.ACCEPTED + project_invitation.save() + serializer = ProjectTeamSerializer(data={'user':new_member.id, 'project':project.id}) + if serializer.is_valid(): + serializer.save() + return Response({"message": "invitation accepted"}, status=status.HTTP_200_OK) + else: + project_invitation.status = InvitationStatus.REJECTED + project_invitation.save() + return Response({"message": "invitation rejected"}, status=status.HTTP_200_OK) \ No newline at end of file From d086eb39b61966ac96311237fbc564b4d7b04789 Mon Sep 17 00:00:00 2001 From: sm4640 Date: Fri, 2 May 2025 22:14:50 +0900 Subject: [PATCH 08/10] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix:=20[#41]=20notif?= =?UTF-8?q?ication=20=EA=B4=80=EB=A0=A8=20=EC=BD=94=EB=93=9C=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- users/models.py | 5 ----- users/serializers.py | 5 ----- users/urls.py | 7 +------ users/views.py | 18 ------------------ 4 files changed, 1 insertion(+), 34 deletions(-) diff --git a/users/models.py b/users/models.py index ac5c4ab..9f62dcd 100644 --- a/users/models.py +++ b/users/models.py @@ -63,8 +63,3 @@ class User(BaseModel, AbstractBaseUser, PermissionsMixin): return self.nickname -class Notification(BaseModel): - user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='notifications') - content = models.TextField() - is_read = models.BooleanField(default=False) - note_type = models.CharField(max_length=10, choices=NotificationType.choices) diff --git a/users/serializers.py b/users/serializers.py index 05f9dc5..817c753 100644 --- a/users/serializers.py +++ b/users/serializers.py @@ -62,8 +62,3 @@ class UserMemberInfoSerializer(serializers.ModelSerializer): 'custom_url', 'job_and_interests' ] - -class NotificationSerializer(serializers.ModelSerializer): - class Meta: - model = Notification - fields = ['id', 'content', 'note_type', 'is_read'] \ No newline at end of file diff --git a/users/urls.py b/users/urls.py index a73f9a1..f7c8f47 100644 --- a/users/urls.py +++ b/users/urls.py @@ -1,14 +1,10 @@ -from django.urls import path, include +from django.urls import path from .views import * -from rest_framework.routers import DefaultRouter app_name = 'users' -router = DefaultRouter() -router.register(r'notifications', NotificationReadViewSet, basename='notification') - urlpatterns = [ path('refresh-token/', RefreshAPIView.as_view()), path('join/', JoinAPIView.as_view()), @@ -20,5 +16,4 @@ urlpatterns = [ path('mypage/profile//', MyPageProfileAPIView.as_view()), path('mypage/works//', MyPageWorkListAPIView.as_view()), path('mypage/my-info/', MyPageMemberInfoAPIView.as_view()), - path('', include(router.urls)), ] \ No newline at end of file diff --git a/users/views.py b/users/views.py index 5713225..3c10136 100644 --- a/users/views.py +++ b/users/views.py @@ -249,21 +249,3 @@ class MyPageMemberInfoAPIView(APIView): return Response(serializer.data, status=status.HTTP_200_OK) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) -class NotificationReadViewSet(ReadOnlyModelViewSet): - serializer_class = NotificationSerializer - - # 30일 이전 알림만 가져옴 - def get_queryset(self): - return UserToNotificationService.get_all_notification(self.request.user) - - def list(self, request, *args, **kwargs): - queryset = self.get_queryset() - serializer = self.get_serializer(queryset, many=True) - return Response(serializer.data, status=status.HTTP_200_OK) - - def retrieve(self, request, *args, **kwargs): - instance = self.get_object() - instance.is_read = True - instance.save() - serializer = self.get_serializer(instance) - return Response(serializer.data, status=status.HTTP_200_OK) \ No newline at end of file From ab8f3afd48ed41a59975d2dd13254a2147951e54 Mon Sep 17 00:00:00 2001 From: sm4640 Date: Fri, 2 May 2025 22:15:54 +0900 Subject: [PATCH 09/10] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix:=20[#41]=20?= =?UTF-8?q?=EC=95=8C=EB=A6=BC=20=EA=B4=80=EB=A0=A8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EB=B0=8F=2030=EC=9D=BC=20=EC=9D=B4?= =?UTF-8?q?=EC=A0=84=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95(lt=20->=20g?= =?UTF-8?q?t)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- users/services.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/users/services.py b/users/services.py index 9ea3ba6..8dee258 100644 --- a/users/services.py +++ b/users/services.py @@ -16,22 +16,17 @@ class CheckUserFieldDuplicateService: @staticmethod def check_custom_url_duplicate(): pass - -# 알림 관련 서비스 로직 -class NotifiationService: - @staticmethod - def set_content_by_type(): - pass + class UserToNotificationService: @staticmethod def get_new_notification_count(user: User): - return user.notifications.filter(created_at__lt=thirty_days_ago, is_read=False).count() + return user.notifications.filter(created_at__gt=thirty_days_ago, is_read=False).count() @staticmethod def get_all_notification(user: User): - return user.notifications.filter(created_at__lt=thirty_days_ago) + return user.notifications.filter(created_at__gt=thirty_days_ago) # 유저 -> 포트폴리오 관련 서비스 로직 @@ -82,4 +77,3 @@ class UserToProjectService: @staticmethod def get_scrap_project(user: User): return user.scrapped_projects.filter(is_published=True) - From 430553f870be114174de62ca77e17deac5b9deae Mon Sep 17 00:00:00 2001 From: sm4640 Date: Fri, 2 May 2025 22:16:27 +0900 Subject: [PATCH 10/10] =?UTF-8?q?=E2=9C=A8=20Feat:=20[#41]=20notification?= =?UTF-8?q?=20=EA=B4=80=EB=A0=A8=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/settings.py | 1 + config/urls.py | 1 + 2 files changed, 2 insertions(+) diff --git a/config/settings.py b/config/settings.py index be08536..f31faa0 100644 --- a/config/settings.py +++ b/config/settings.py @@ -56,6 +56,7 @@ INSTALLED_APPS = [ 'portfolios', 'projects', 'codes', + 'notifications', ] MIDDLEWARE = [ diff --git a/config/urls.py b/config/urls.py index af7d012..b17ad1b 100644 --- a/config/urls.py +++ b/config/urls.py @@ -10,6 +10,7 @@ urlpatterns = [ # path('api/code/', include('codes.urls')), path('api/portfolio/', include('portfolios.urls')), path('api/project/', include('projects.urls')), + path('api/notification/', include('notifications.urls')), ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) if settings.DEBUG: