Skip to content

Commit ddb4a43

Browse files
committed
refactor: 어드민 경로 일원화
1 parent e837e28 commit ddb4a43

5 files changed

Lines changed: 72 additions & 52 deletions

File tree

app/admin_api/test/socialaccount_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ def test_social_account_provider_filter_enum_exposed_in_openapi(api_client):
165165
assert response.status_code == http.HTTPStatus.OK
166166
schema = yaml.safe_load(response.content)
167167

168-
list_path = next(p for p in schema["paths"] if p.endswith("/allauth/social-account/"))
168+
list_path = next(p for p in schema["paths"] if p.endswith("/allauth/socialaccount/"))
169169
params = schema["paths"][list_path]["get"]["parameters"]
170170
provider = next(p for p in params if p["name"] == "provider")
171171
assert provider["schema"]["items"]["enum"] == ["google", "kakao"]

app/admin_api/urls.py

Lines changed: 41 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,16 @@
4242
from django.urls import include, path
4343
from rest_framework import routers
4444

45+
# 라우트 컨벤션: <app_label 첫 segment>/<model_name>. (프론트 ChoicePicker 가 selectables 라우트를 모델 메타로 유도)
46+
# 모델 없는 뷰셋(order-notifications, refund-authorizer, charts)은 유도 대상이 아니므로 기존 명시 라우트 유지.
47+
4548
admin_user_router = routers.SimpleRouter()
4649
admin_user_router.register("userext", UserAdminViewSet, basename="admin-user")
4750
admin_user_router.register("organization", OrganizationAdminViewSet, basename="admin-organization")
48-
admin_user_router.register("mcp-token", McpTokenAdminViewSet, basename="admin-mcp-token")
51+
admin_user_router.register("mcptoken", McpTokenAdminViewSet, basename="admin-mcp-token")
4952

5053
admin_cms_router = routers.SimpleRouter()
51-
admin_cms_router.register("domain-group", DomainGroupAdminViewSet, basename="admin-domain-group")
54+
admin_cms_router.register("domaingroup", DomainGroupAdminViewSet, basename="admin-domain-group")
5255
admin_cms_router.register("sitemap", SitemapAdminViewSet, basename="admin-sitemap")
5356
admin_cms_router.register("page", PageAdminViewSet, basename="admin-page")
5457

@@ -67,76 +70,74 @@
6770
admin_event_router.register("room", RoomAdminViewSet)
6871
admin_event_router.register("roomschedule", RoomScheduleAdminViewSet)
6972

70-
admin_modificationaudit_router = routers.SimpleRouter()
71-
admin_modificationaudit_router.register(
72-
"modification-audit", ModificationAuditAdminViewSet, basename="admin-modification-audit"
73+
admin_participant_portal_router = routers.SimpleRouter()
74+
admin_participant_portal_router.register(
75+
"modificationaudit", ModificationAuditAdminViewSet, basename="admin-modification-audit"
7376
)
7477

75-
admin_notification_email_router = routers.SimpleRouter()
76-
admin_notification_email_router.register(
77-
"template", EmailNotificationTemplateAdminViewSet, basename="admin-notification-email-template"
78+
admin_notification_router = routers.SimpleRouter()
79+
admin_notification_router.register(
80+
"emailnotificationtemplate", EmailNotificationTemplateAdminViewSet, basename="admin-notification-email-template"
7881
)
79-
admin_notification_email_router.register(
80-
"history", EmailNotificationHistoryAdminViewSet, basename="admin-notification-email-history"
82+
admin_notification_router.register(
83+
"emailnotificationhistory", EmailNotificationHistoryAdminViewSet, basename="admin-notification-email-history"
8184
)
82-
83-
admin_notification_kakao_router = routers.SimpleRouter()
84-
admin_notification_kakao_router.register(
85-
"template",
85+
admin_notification_router.register(
86+
"nhncloudkakaoalimtalknotificationtemplate",
8687
NHNCloudKakaoAlimTalkNotificationTemplateAdminViewSet,
8788
basename="admin-notification-kakao-template",
8889
)
89-
admin_notification_kakao_router.register(
90-
"history",
90+
admin_notification_router.register(
91+
"nhncloudkakaoalimtalknotificationhistory",
9192
NHNCloudKakaoAlimTalkNotificationHistoryAdminViewSet,
9293
basename="admin-notification-kakao-history",
9394
)
94-
95-
admin_notification_sms_router = routers.SimpleRouter()
96-
admin_notification_sms_router.register(
97-
"template", NHNCloudSMSNotificationTemplateAdminViewSet, basename="admin-notification-sms-template"
95+
admin_notification_router.register(
96+
"nhncloudsmsnotificationtemplate",
97+
NHNCloudSMSNotificationTemplateAdminViewSet,
98+
basename="admin-notification-sms-template",
9899
)
99-
admin_notification_sms_router.register(
100-
"history", NHNCloudSMSNotificationHistoryAdminViewSet, basename="admin-notification-sms-history"
100+
admin_notification_router.register(
101+
"nhncloudsmsnotificationhistory",
102+
NHNCloudSMSNotificationHistoryAdminViewSet,
103+
basename="admin-notification-sms-history",
101104
)
102105

103-
admin_external_api_google_router = routers.SimpleRouter()
104-
admin_external_api_google_router.register("oauth2", GoogleOAuth2AdminViewSet, basename="admin-google-oauth2")
106+
admin_external_api_router = routers.SimpleRouter()
107+
admin_external_api_router.register("googleoauth2", GoogleOAuth2AdminViewSet, basename="admin-google-oauth2")
105108

106109
admin_shop_router = routers.SimpleRouter()
107-
admin_shop_router.register("orders", OrderAdminViewSet, basename="admin-shop-order")
110+
admin_shop_router.register("order", OrderAdminViewSet, basename="admin-shop-order")
108111
admin_shop_router.register(
109112
"order-notifications", OrderNotificationAdminViewSet, basename="admin-shop-order-notification"
110113
)
111-
admin_shop_router.register("products", ProductAdminViewSet, basename="admin-shop-product")
112-
admin_shop_router.register("categories", CategoryAdminViewSet, basename="admin-shop-category")
113-
admin_shop_router.register("tags", TagAdminViewSet, basename="admin-shop-tag")
114-
admin_shop_router.register("category-groups", CategoryGroupAdminViewSet, basename="admin-shop-category-group")
115-
admin_shop_router.register("option-groups", OptionGroupAdminViewSet, basename="admin-shop-option-group")
114+
admin_shop_router.register("product", ProductAdminViewSet, basename="admin-shop-product")
115+
admin_shop_router.register("category", CategoryAdminViewSet, basename="admin-shop-category")
116+
admin_shop_router.register("tag", TagAdminViewSet, basename="admin-shop-tag")
117+
admin_shop_router.register("categorygroup", CategoryGroupAdminViewSet, basename="admin-shop-category-group")
118+
admin_shop_router.register("optiongroup", OptionGroupAdminViewSet, basename="admin-shop-option-group")
116119
admin_shop_router.register("refund-authorizer", RefundAuthorizerAdminViewSet, basename="admin-shop-refund-authorizer")
117120

118121
admin_document_router = routers.SimpleRouter()
119-
admin_document_router.register("templates", DocumentTemplateAdminViewSet, basename="admin-document-template")
120-
admin_document_router.register("issued", IssuedDocumentAdminViewSet, basename="admin-document-issued")
122+
admin_document_router.register("documenttemplate", DocumentTemplateAdminViewSet, basename="admin-document-template")
123+
admin_document_router.register("issueddocument", IssuedDocumentAdminViewSet, basename="admin-document-issued")
121124

122125
admin_dashboard_router = routers.SimpleRouter()
123126
admin_dashboard_router.register("charts", DashboardChartAdminViewSet, basename="admin-dashboard-chart")
124127

125128
admin_allauth_router = routers.SimpleRouter()
126-
admin_allauth_router.register("social-app", SocialAppAdminViewSet, basename="admin-social-app")
127-
admin_allauth_router.register("social-account", SocialAccountAdminViewSet, basename="admin-social-account")
128-
admin_allauth_router.register("email-address", EmailAddressAdminViewSet, basename="admin-email-address")
129+
admin_allauth_router.register("socialapp", SocialAppAdminViewSet, basename="admin-social-app")
130+
admin_allauth_router.register("socialaccount", SocialAccountAdminViewSet, basename="admin-social-account")
131+
admin_allauth_router.register("emailaddress", EmailAddressAdminViewSet, basename="admin-email-address")
129132

130133
urlpatterns = [
131134
path("cms/", include(admin_cms_router.urls)),
132135
path("file/", include(admin_file_router.urls)),
133136
path("user/", include(admin_user_router.urls)),
134137
path("event/", include(admin_event_router.urls)),
135-
path("modification-audit/", include(admin_modificationaudit_router.urls)),
136-
path("notification/email/", include(admin_notification_email_router.urls)),
137-
path("notification/kakao-alimtalk/", include(admin_notification_kakao_router.urls)),
138-
path("notification/sms/", include(admin_notification_sms_router.urls)),
139-
path("external-api/google/", include(admin_external_api_google_router.urls)),
138+
path("participant_portal_api/", include(admin_participant_portal_router.urls)),
139+
path("notification/", include(admin_notification_router.urls)),
140+
path("external_api/", include(admin_external_api_router.urls)),
140141
path("shop/", include(admin_shop_router.urls)),
141142
path("document/", include(admin_document_router.urls)),
142143
path("dashboard/", include(admin_dashboard_router.urls)),

app/core/openapi/ui_hints.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,14 @@ def ui_hints_for_model_field(model_field: object) -> dict:
99
hints: dict = {}
1010

1111
if isinstance(model_field, (ForeignKey, ManyToManyField)):
12+
hints["ui:options"] = {
13+
"choiceApp": model_field.related_model._meta.app_config.name.split(".")[0],
14+
"choiceResource": model_field.related_model._meta.model_name,
15+
}
1216
if meta_schema := getattr(model_field.related_model, "choices_meta_schema", None):
1317
if hasattr(model_field.related_model, "get_audit_choice_meta"):
1418
meta_schema = meta_schema | AUDIT_CHOICE_META_SCHEMA
15-
hints["ui:options"] = {"choiceMetaSchema": meta_schema}
19+
hints["ui:options"]["choiceMetaSchema"] = meta_schema
1620

1721
if isinstance(model_field, ManyToManyField):
1822
return hints | {"ui:field": "m2m_select"}

app/core/viewset/json_schema_viewset.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,3 +155,18 @@ def response_json_schema(self, *args: tuple, **kwargs: dict) -> response.Respons
155155
@decorators.action(detail=False, methods=["get"], url_path="choices")
156156
def response_choices(self, *args: tuple, **kwargs: dict) -> response.Response:
157157
return response.Response(data=self.get_choices())
158+
159+
@utils.extend_schema(
160+
tags=[OpenAPITag.ADMIN_JSON_SCHEMA],
161+
summary="Selectables (this model's instances as choices)",
162+
responses={status.HTTP_200_OK: openapi.OpenApiResponse(response=types.OpenApiTypes.OBJECT)},
163+
)
164+
@decorators.action(detail=False, methods=["get"], url_path="selectables")
165+
def response_selectables(self, *args: tuple, **kwargs: dict) -> response.Response:
166+
qs = self.get_queryset()
167+
return response.Response(
168+
data={
169+
"results": self._get_choices_from_queryset(qs, False),
170+
"meta_schema": getattr(qs.model, "choices_meta_schema", None) or {},
171+
}
172+
)

mcp_app/routes.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def route_map(self) -> RouteMap:
3232
# ── CMS 도메인 그룹(어드민 읽기) ──
3333
Route(
3434
method="GET",
35-
path="/v1/admin-api/cms/domain-group/",
35+
path="/v1/admin-api/cms/domaingroup/",
3636
summary="도메인 그룹 목록",
3737
description="프론트엔드 도메인 그룹(name + domains 호스트 목록)을 조회합니다. "
3838
"CMS 페이지/사이트맵의 domain_group 이 어떤 실제 도메인(예: 2026.pycon.kr)에 대응하는지 확인하고, "
@@ -140,54 +140,54 @@ def route_map(self) -> RouteMap:
140140
# ── 쇼핑 주문·상품(어드민 읽기 전용) ──
141141
Route(
142142
method="GET",
143-
path="/v1/admin-api/shop/category-groups/",
143+
path="/v1/admin-api/shop/categorygroup/",
144144
summary="카테고리 그룹 목록",
145145
description='티켓 카테고리 그룹(예: "2024"/"2025"/"2026") 목록(id+이름). '
146146
"여기서 얻은 id 로 상품/주문을 category_group 으로 필터해 연도를 구분하세요.",
147147
),
148148
Route(
149149
method="GET",
150-
path="/v1/admin-api/shop/orders/",
150+
path="/v1/admin-api/shop/order/",
151151
summary="주문 목록",
152152
description="주문을 조회/필터합니다(읽기 전용, 페이지네이션 count/results). 연도 구분은 category_group=<그룹 id> 로.",
153153
),
154154
Route(
155155
method="GET",
156-
path="/v1/admin-api/shop/orders/{id}/",
156+
path="/v1/admin-api/shop/order/{id}/",
157157
summary="주문 상세",
158158
),
159159
Route(
160160
method="GET",
161-
path="/v1/admin-api/shop/products/",
161+
path="/v1/admin-api/shop/product/",
162162
summary="상품 목록",
163163
description="상품을 조회/필터합니다(읽기 전용). 연도 구분은 category_group=<그룹 id> 로.",
164164
),
165165
Route(
166166
method="GET",
167-
path="/v1/admin-api/shop/products/{id}/",
167+
path="/v1/admin-api/shop/product/{id}/",
168168
summary="상품 상세",
169169
),
170170
Route(
171171
method="GET",
172-
path="/v1/admin-api/shop/products/choices/",
172+
path="/v1/admin-api/shop/product/choices/",
173173
summary="상품 관계 선택지",
174174
description="상품의 FK/M2M 선택지(category, tag 등; const=id, title=표시명). category id 로 상품/주문을 필터할 수 있습니다.",
175175
),
176176
Route(
177177
method="GET",
178-
path="/v1/admin-api/shop/categories/",
178+
path="/v1/admin-api/shop/category/",
179179
summary="카테고리 목록",
180180
description="카테고리 목록(id, 이름, group, event, is_ticket). group/event/is_ticket 으로 필터 가능. "
181181
"상품/주문을 category=<id> 로 필터할 때 id 출처.",
182182
),
183183
Route(
184184
method="GET",
185-
path="/v1/admin-api/shop/categories/{id}/",
185+
path="/v1/admin-api/shop/category/{id}/",
186186
summary="카테고리 상세",
187187
),
188188
Route(
189189
method="GET",
190-
path="/v1/admin-api/shop/categories/choices/",
190+
path="/v1/admin-api/shop/category/choices/",
191191
summary="카테고리 관계 선택지",
192192
description="카테고리의 FK 선택지: group(카테고리 그룹=연도)·event(const=id, title=표시명).",
193193
),

0 commit comments

Comments
 (0)