Skip to content

Commit f2e1bf1

Browse files
committed
feat: event 하위 항목에 대한 admin-api 추가
1 parent 6b5929f commit f2e1bf1

9 files changed

Lines changed: 302 additions & 1 deletion

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from core.const.serializer import COMMON_ADMIN_FIELDS
2+
from core.serializer.base_abstract_serializer import BaseAbstractSerializer
3+
from core.serializer.json_schema_serializer import JsonSchemaSerializer
4+
from event.models import Event
5+
from rest_framework import serializers
6+
7+
8+
class EventAdminSerializer(BaseAbstractSerializer, JsonSchemaSerializer, serializers.ModelSerializer):
9+
class Meta:
10+
model = Event
11+
fields = COMMON_ADMIN_FIELDS + ("organization", "name_ko", "name_en")
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from core.const.serializer import COMMON_ADMIN_FIELDS
2+
from core.serializer.base_abstract_serializer import BaseAbstractSerializer
3+
from core.serializer.json_schema_serializer import JsonSchemaSerializer
4+
from event.presentation.models import Presentation, PresentationCategory, PresentationSpeaker, PresentationType
5+
from rest_framework import serializers
6+
7+
8+
class PresentationTypeAdminSerializer(BaseAbstractSerializer, JsonSchemaSerializer, serializers.ModelSerializer):
9+
class Meta:
10+
model = PresentationType
11+
fields = COMMON_ADMIN_FIELDS + ("event", "name_ko", "name_en")
12+
13+
14+
class PresentationCategoryAdminSerializer(BaseAbstractSerializer, JsonSchemaSerializer, serializers.ModelSerializer):
15+
class Meta:
16+
model = PresentationCategory
17+
fields = COMMON_ADMIN_FIELDS + ("type", "name_ko", "name_en")
18+
19+
20+
class PresentationAdminSerializer(BaseAbstractSerializer, JsonSchemaSerializer, serializers.ModelSerializer):
21+
class Meta:
22+
model = Presentation
23+
fields = COMMON_ADMIN_FIELDS + ("title_ko", "title_en")
24+
25+
26+
class PresentationSpeakerAdminSerializer(BaseAbstractSerializer, JsonSchemaSerializer, serializers.ModelSerializer):
27+
class Meta:
28+
model = PresentationSpeaker
29+
fields = COMMON_ADMIN_FIELDS + ("presentation", "user", "biography_ko", "biography_en")
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from core.const.serializer import COMMON_ADMIN_FIELDS
2+
from core.serializer.base_abstract_serializer import BaseAbstractSerializer
3+
from core.serializer.json_schema_serializer import JsonSchemaSerializer
4+
from event.sponsor.models import Sponsor, SponsorTier
5+
from rest_framework import serializers
6+
7+
8+
class SponsorTierAdminSerializer(BaseAbstractSerializer, JsonSchemaSerializer, serializers.ModelSerializer):
9+
class Meta:
10+
model = SponsorTier
11+
fields = COMMON_ADMIN_FIELDS + ("event", "name_ko", "name_en", "order")
12+
13+
14+
class SponsorAdminSerializer(BaseAbstractSerializer, JsonSchemaSerializer, serializers.ModelSerializer):
15+
class Meta:
16+
model = Sponsor
17+
fields = COMMON_ADMIN_FIELDS + ("event", "logo", "page", "name_ko", "name_en")

app/admin_api/urls.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
from admin_api.views.cms import PageAdminViewSet, SitemapAdminViewSet
2+
from admin_api.views.event.event import EventAdminViewSet
3+
from admin_api.views.event.presentation import (
4+
PresentationAdminViewSet,
5+
PresentationSpeakerAdminViewSet,
6+
PresentationTypeAdminViewSet,
7+
)
8+
from admin_api.views.event.sponsor import SponsorAdminViewSet, SponsorTierAdminViewSet
29
from admin_api.views.file import PublicFileAdminViewSet
310
from admin_api.views.user import UserAdminViewSet
411
from django.urls import include, path
@@ -14,8 +21,20 @@
1421
admin_file_router = routers.SimpleRouter()
1522
admin_file_router.register("publicfile", PublicFileAdminViewSet, basename="admin-public-file")
1623

24+
admin_event_router = routers.SimpleRouter()
25+
admin_event_router.register("event", EventAdminViewSet)
26+
admin_event_router.register("sponsortier", SponsorTierAdminViewSet)
27+
admin_event_router.register("sponsor", SponsorAdminViewSet)
28+
admin_event_router.register("presentationtype", PresentationTypeAdminViewSet)
29+
admin_event_router.register("presentation", PresentationAdminViewSet)
30+
admin_event_router.register(
31+
"presentation/(?P<presentation_id>{UUID_V4_PATTERN})/speaker",
32+
PresentationSpeakerAdminViewSet,
33+
)
34+
1735
urlpatterns = [
1836
path("cms/", include(admin_cms_router.urls)),
1937
path("file/", include(admin_file_router.urls)),
2038
path("user/", include(admin_user_router.urls)),
39+
path("event/", include(admin_event_router.urls)),
2140
]

app/admin_api/views/event/event.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from __future__ import annotations
2+
3+
from admin_api.serializers.event.event import EventAdminSerializer
4+
from core.const.tag import OpenAPITag
5+
from core.permissions import IsSuperUser
6+
from core.viewset.json_schema_viewset import JsonSchemaViewSet
7+
from drf_spectacular.utils import extend_schema, extend_schema_view
8+
from event.models import Event
9+
from rest_framework import viewsets
10+
11+
ADMIN_METHODS = ["list", "retrieve", "create", "update", "partial_update", "destroy"]
12+
13+
14+
@extend_schema_view(**{m: extend_schema(tags=[OpenAPITag.ADMIN_EVENT_EVENT]) for m in ADMIN_METHODS})
15+
class EventAdminViewSet(JsonSchemaViewSet, viewsets.ModelViewSet):
16+
http_method_names = ["get", "post", "patch", "delete"]
17+
serializer_class = EventAdminSerializer
18+
permission_classes = [IsSuperUser]
19+
queryset = Event.objects.filter_active().select_related("created_by", "updated_by", "deleted_by")
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
from __future__ import annotations
2+
3+
from admin_api.serializers.event.presentation import (
4+
PresentationAdminSerializer,
5+
PresentationCategoryAdminSerializer,
6+
PresentationSpeakerAdminSerializer,
7+
PresentationTypeAdminSerializer,
8+
)
9+
from core.const.regex import UUID_V4_PATTERN
10+
from core.const.tag import OpenAPITag
11+
from core.permissions import IsSuperUser
12+
from core.viewset.json_schema_viewset import JsonSchemaViewSet
13+
from django.db import models
14+
from drf_spectacular.utils import extend_schema, extend_schema_view
15+
from event.presentation.models import (
16+
Presentation,
17+
PresentationCategory,
18+
PresentationCategoryRelation,
19+
PresentationSpeaker,
20+
PresentationType,
21+
)
22+
from rest_framework import decorators, exceptions, request, response, status, viewsets
23+
24+
ADMIN_METHODS = ["list", "retrieve", "create", "update", "partial_update", "destroy"]
25+
26+
27+
@extend_schema_view(**{m: extend_schema(tags=[OpenAPITag.ADMIN_EVENT_PRESENTATION]) for m in ADMIN_METHODS})
28+
class PresentationTypeAdminViewSet(JsonSchemaViewSet, viewsets.ModelViewSet):
29+
http_method_names = ["get", "post", "patch", "delete"]
30+
serializer_class = PresentationTypeAdminSerializer
31+
permission_classes = [IsSuperUser]
32+
queryset = PresentationType.objects.filter_active().select_related("created_by", "updated_by", "deleted_by")
33+
34+
@extend_schema(
35+
tags=[OpenAPITag.ADMIN_EVENT_PRESENTATION],
36+
responses={status.HTTP_200_OK: PresentationCategoryAdminSerializer(many=True)},
37+
)
38+
@decorators.action(detail=True, methods=["get"], url_path="categories")
39+
def list_categories(self, *args: tuple, **kwargs: dict) -> response.Response:
40+
categories = PresentationCategory.objects.filter_active().filter(type=self.get_object())
41+
return response.Response(data=PresentationCategoryAdminSerializer(instance=categories, many=True).data)
42+
43+
@extend_schema(
44+
tags=[OpenAPITag.ADMIN_EVENT_PRESENTATION],
45+
request=PresentationCategoryAdminSerializer,
46+
responses={status.HTTP_201_CREATED: PresentationCategoryAdminSerializer},
47+
)
48+
@decorators.action(detail=True, methods=["post"], url_path="categories")
49+
def add_category(self, request: request.Request, *args: tuple, **kwargs: dict) -> response.Response:
50+
serializer = PresentationCategoryAdminSerializer(data=request.data)
51+
serializer.is_valid(raise_exception=True)
52+
serializer.save()
53+
return response.Response(data=serializer.data, status=status.HTTP_201_CREATED)
54+
55+
@extend_schema(
56+
tags=[OpenAPITag.ADMIN_EVENT_PRESENTATION],
57+
request=PresentationCategoryAdminSerializer,
58+
responses={status.HTTP_200_OK: PresentationCategoryAdminSerializer},
59+
)
60+
@decorators.action(detail=True, methods=["patch"], url_path=f"categories/(?P<category_id>{UUID_V4_PATTERN})")
61+
def update_category(self, request: request.Request, pk: str, category_id: str, *args, **kwargs):
62+
if not (category := PresentationCategory.objects.filter_active().filter(type_id=pk, id=category_id).first()):
63+
raise exceptions.NotFound(detail="Category not found.")
64+
65+
serializer = PresentationCategoryAdminSerializer(instance=category, data=request.data, partial=True)
66+
serializer.is_valid(raise_exception=True)
67+
serializer.save()
68+
return response.Response(data=serializer.data)
69+
70+
@extend_schema(tags=[OpenAPITag.ADMIN_EVENT_PRESENTATION], responses={status.HTTP_204_NO_CONTENT: None})
71+
@decorators.action(detail=True, methods=["delete"], url_path=f"categories/(?P<category_id>{UUID_V4_PATTERN})")
72+
def delete_category(self, pk: str, category_id: str, *args: tuple, **kwargs: dict) -> response.Response:
73+
if not (category := PresentationCategory.objects.filter_active().filter(type_id=pk, id=category_id).first()):
74+
raise exceptions.NotFound(detail="Category not found.")
75+
76+
category.delete()
77+
return response.Response(status=status.HTTP_204_NO_CONTENT)
78+
79+
80+
@extend_schema_view(**{m: extend_schema(tags=[OpenAPITag.ADMIN_EVENT_PRESENTATION]) for m in ADMIN_METHODS})
81+
class PresentationAdminViewSet(JsonSchemaViewSet, viewsets.ModelViewSet):
82+
http_method_names = ["get", "post", "patch", "delete"]
83+
serializer_class = PresentationAdminSerializer
84+
permission_classes = [IsSuperUser]
85+
queryset = Presentation.objects.get_all_nested_data().select_related("created_by", "updated_by", "deleted_by")
86+
87+
@extend_schema(
88+
tags=[OpenAPITag.ADMIN_EVENT_PRESENTATION],
89+
responses={status.HTTP_200_OK: PresentationCategoryAdminSerializer(many=True)},
90+
)
91+
@decorators.action(detail=True, methods=["get"], url_path="categories")
92+
def list_categories(self, *args: tuple, **kwargs: dict) -> response.Response:
93+
categories = self.get_object().active_categories
94+
return response.Response(data=PresentationCategoryAdminSerializer(instance=categories, many=True).data)
95+
96+
@extend_schema(tags=[OpenAPITag.ADMIN_EVENT_PRESENTATION], responses={status.HTTP_201_CREATED: None})
97+
@decorators.action(detail=True, methods=["post"], url_path="categories/(?P<category_id>{UUID_V4_PATTERN})")
98+
def add_category(self, pk: str, category_id: str, *args: tuple, **kwargs: dict) -> response.Response:
99+
PresentationCategoryRelation.objects.get_or_create(presentation_id=pk, category_id=category_id)
100+
return response.Response(status=status.HTTP_201_CREATED)
101+
102+
@extend_schema(tags=[OpenAPITag.ADMIN_EVENT_PRESENTATION], responses={status.HTTP_204_NO_CONTENT: None})
103+
@decorators.action(detail=True, methods=["delete"], url_path="categories/(?P<category_id>{UUID_V4_PATTERN})")
104+
def remove_category(self, pk: str, category_id: str, *args: tuple, **kwargs: dict) -> response.Response:
105+
if not (
106+
relation := PresentationCategoryRelation.objects.filter(presentation_id=pk, category_id=category_id).first()
107+
):
108+
raise exceptions.NotFound(detail="Category is not associated with this presentation.")
109+
110+
relation.delete()
111+
return response.Response(status=status.HTTP_204_NO_CONTENT)
112+
113+
114+
@extend_schema_view(**{m: extend_schema(tags=[OpenAPITag.ADMIN_EVENT_PRESENTATION]) for m in ADMIN_METHODS})
115+
class PresentationSpeakerAdminViewSet(JsonSchemaViewSet, viewsets.ModelViewSet):
116+
http_method_names = ["get", "post", "patch", "delete"]
117+
serializer_class = PresentationSpeakerAdminSerializer
118+
permission_classes = [IsSuperUser]
119+
queryset = PresentationSpeaker.objects.filter_active().select_related("created_by", "updated_by", "deleted_by")
120+
121+
def get_queryset(self) -> models.QuerySet[PresentationSpeaker]:
122+
return super().get_queryset().filter(presentation_id=self.kwargs["presentation_id"])
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
from __future__ import annotations
2+
3+
from admin_api.serializers.event.sponsor import SponsorAdminSerializer, SponsorTierAdminSerializer
4+
from core.const.regex import UUID_V4_PATTERN
5+
from core.const.tag import OpenAPITag
6+
from core.permissions import IsSuperUser
7+
from core.viewset.json_schema_viewset import JsonSchemaViewSet
8+
from django.db import models
9+
from drf_spectacular.utils import extend_schema, extend_schema_view
10+
from event.sponsor.models import Sponsor, SponsorTier, SponsorTierSponsorRelation
11+
from rest_framework import decorators, exceptions, response, status, viewsets
12+
13+
ADMIN_METHODS = ["list", "retrieve", "create", "update", "partial_update", "destroy"]
14+
15+
16+
@extend_schema_view(**{m: extend_schema(tags=[OpenAPITag.ADMIN_EVENT_SPONSOR]) for m in ADMIN_METHODS})
17+
class SponsorTierAdminViewSet(JsonSchemaViewSet, viewsets.ModelViewSet):
18+
http_method_names = ["get", "post", "patch", "delete"]
19+
serializer_class = SponsorTierAdminSerializer
20+
permission_classes = [IsSuperUser]
21+
queryset = (
22+
SponsorTier.objects.filter_active()
23+
.prefetch_related(
24+
models.Prefetch(
25+
lookup="sponsors",
26+
queryset=Sponsor.objects.filter_active().select_related("created_by", "updated_by", "deleted_by"),
27+
to_attr="_prefetched_active_sponsors",
28+
),
29+
)
30+
.select_related("created_by", "updated_by", "deleted_by")
31+
)
32+
33+
@extend_schema(
34+
tags=[OpenAPITag.ADMIN_EVENT_SPONSOR],
35+
responses={status.HTTP_200_OK: SponsorAdminSerializer(many=True)},
36+
)
37+
@decorators.action(detail=True, methods=["get"], url_path="sponsors")
38+
def list_sponsors(self, *args: tuple, **kwargs: dict) -> response.Response:
39+
tier: SponsorTier = self.get_object()
40+
return response.Response(data=SponsorAdminSerializer(instance=tier.active_sponsors, many=True).data)
41+
42+
@extend_schema(tags=[OpenAPITag.ADMIN_EVENT_SPONSOR], responses={status.HTTP_201_CREATED: SponsorAdminSerializer})
43+
@decorators.action(detail=True, methods=["post"], url_path=f"sponsors/(?P<sponsor_id>{UUID_V4_PATTERN})")
44+
def add_sponsor(self, sponsor_id: str, *args: tuple, **kwargs: dict) -> response.Response:
45+
tier: SponsorTier = self.get_object()
46+
if not (sponsor := Sponsor.objects.filter_active().filter(id=sponsor_id).first()):
47+
raise exceptions.NotFound(detail="Sponsor not found")
48+
49+
SponsorTierSponsorRelation.objects.get_or_create(tier=tier, sponsor=sponsor)
50+
return response.Response(data=SponsorAdminSerializer(instance=sponsor).data, status=status.HTTP_201_CREATED)
51+
52+
@extend_schema(tags=[OpenAPITag.ADMIN_EVENT_SPONSOR], responses={status.HTTP_204_NO_CONTENT: None})
53+
@decorators.action(detail=True, methods=["delete"], url_path=f"sponsors/(?P<sponsor_id>{UUID_V4_PATTERN})")
54+
def remove_sponsor(self, pk: str, sponsor_id: str, *args: tuple, **kwargs: dict) -> response.Response:
55+
if not (relation := SponsorTierSponsorRelation.objects.filter(tier_id=pk, sponsor_id=sponsor_id).first()):
56+
raise exceptions.NotFound(detail="Sponsor is not associated with this tier")
57+
58+
relation.delete()
59+
return response.Response(status=status.HTTP_204_NO_CONTENT)
60+
61+
62+
@extend_schema_view(**{m: extend_schema(tags=[OpenAPITag.ADMIN_EVENT_SPONSOR]) for m in ADMIN_METHODS})
63+
class SponsorAdminViewSet(JsonSchemaViewSet, viewsets.ModelViewSet):
64+
http_method_names = ["get", "post", "patch", "delete"]
65+
serializer_class = SponsorAdminSerializer
66+
permission_classes = [IsSuperUser]
67+
queryset = Sponsor.objects.filter_active().select_related("created_by", "updated_by", "deleted_by")
68+
69+
@extend_schema(
70+
tags=[OpenAPITag.ADMIN_EVENT_SPONSOR],
71+
responses={status.HTTP_200_OK: SponsorTierAdminSerializer(many=True)},
72+
)
73+
@decorators.action(detail=True, methods=["get"], url_path="tiers")
74+
def list_tiers(self, *args: tuple, **kwargs: dict) -> response.Response:
75+
sponsor: Sponsor = self.get_object()
76+
tier_id_qs = SponsorTierSponsorRelation.objects.filter(sponsor=sponsor).values_list("tier_id", flat=True)
77+
tiers = SponsorTier.objects.filter_active().filter(id__in=tier_id_qs)
78+
return response.Response(data=SponsorTierAdminSerializer(instance=tiers, many=True).data)

app/core/const/regex.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
import re
22

3-
UUID_V4 = re.compile("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", re.IGNORECASE)
3+
UUID_V4_PATTERN = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
4+
UUID_V4_REGEX = re.compile(f"^{UUID_V4_PATTERN}$", re.IGNORECASE)

app/core/const/tag.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
class OpenAPITag:
22
CMS = "CMS"
3+
EVENT_PRESENTATION = "Event > Presentation"
4+
EVENT_SPONSOR = "Event > Sponsor"
35

46
ADMIN_ACCOUNT = "Admin > Sign-In & Sign-Out"
57
ADMIN_USER = "Admin > User"
68
ADMIN_CMS = "Admin > CMS"
79
ADMIN_PUBLIC_FILE = "Admin > Public File"
10+
ADMIN_EVENT_EVENT = "Admin > Event > Event"
11+
ADMIN_EVENT_PRESENTATION = "Admin > Event > Presentation"
12+
ADMIN_EVENT_SPONSOR = "Admin > Event > Sponsor"
813
ADMIN_JSON_SCHEMA = "Admin > JSON Schema"

0 commit comments

Comments
 (0)