Skip to content

Commit cec6763

Browse files
committed
feat: sitemap 클릭 시 외부 링크로 갈 수 있도록 기능 추가
1 parent 10f756f commit cec6763

File tree

4 files changed

+147
-12
lines changed

4 files changed

+147
-12
lines changed

app/admin_api/serializers/cms.py

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import re
2+
13
from cms.models import Page, Section, Sitemap
24
from core.const.serializer import COMMON_ADMIN_FIELDS
35
from core.serializer.base_abstract_serializer import BaseAbstractSerializer
@@ -8,25 +10,64 @@
810
class SitemapAdminSerializer(BaseAbstractSerializer, JsonSchemaSerializer, serializers.ModelSerializer):
911
class Meta:
1012
model = Sitemap
11-
fields = COMMON_ADMIN_FIELDS + ("parent_sitemap", "route_code", "order", "page", "name_ko", "name_en", "hide")
13+
fields = COMMON_ADMIN_FIELDS + (
14+
"parent_sitemap",
15+
"route_code",
16+
"order",
17+
"page",
18+
"external_link",
19+
"is_frontend_page",
20+
"name_ko",
21+
"name_en",
22+
"hide",
23+
)
24+
25+
def validate_route_code(self, value: str) -> str:
26+
if not re.match(r"^[a-zA-Z0-9_-]*$", value):
27+
raise serializers.ValidationError("route_code는 알파벳, 숫자, 언더바(_)로만 구성되어야 합니다.")
28+
29+
return value
30+
31+
def validate_external_link(self, value: str | None) -> str | None:
32+
# 빈 string인 경우 None으로 처리
33+
return value or None
34+
35+
def validate_parent_sitemap(self, value: Sitemap | None) -> Sitemap | None:
36+
if not value:
37+
return None
38+
39+
if parent_sitemap := self.instance:
40+
while parent_sitemap:
41+
if value == parent_sitemap:
42+
raise serializers.ValidationError("Parent Sitemap이 본인 또는 자식 Sitemap을 가리킬 수 없습니다.")
43+
parent_sitemap = parent_sitemap.parent_sitemap
44+
45+
return value
46+
47+
def validate(self, attrs: dict) -> dict:
48+
page = attrs.get("page", getattr(self.instance, "page", None))
49+
external_link = attrs.get("external_link", getattr(self.instance, "external_link", None))
50+
is_frontend_page = attrs.get("is_frontend_page", getattr(self.instance, "is_frontend_page", False))
51+
52+
if not (page or external_link or is_frontend_page):
53+
raise serializers.ValidationError(
54+
"Page 또는 External Link, is_frontend_page 중 하나는 반드시 선택 또는 입력해야 합니다."
55+
)
56+
if len([v for v in (page, external_link, is_frontend_page) if v]) > 1:
57+
raise serializers.ValidationError("Page, External Link, is_frontend_page 중 하나만 선택할 수 있습니다.")
58+
59+
return attrs
1260

1361

1462
class PageAdminSerializer(BaseAbstractSerializer, JsonSchemaSerializer, serializers.ModelSerializer):
1563
class Meta:
1664
model = Page
17-
fields = COMMON_ADMIN_FIELDS + (
18-
"title_ko",
19-
"title_en",
20-
"subtitle_ko",
21-
"subtitle_en",
22-
"show_top_title_banner",
23-
"show_bottom_sponsor_banner",
24-
)
65+
fields = COMMON_ADMIN_FIELDS + ("title_ko", "title_en", "show_top_title_banner", "show_bottom_sponsor_banner")
2566

2667

2768
class SectionAdminSerializer(BaseAbstractSerializer, JsonSchemaSerializer, serializers.ModelSerializer):
2869
page = serializers.PrimaryKeyRelatedField(queryset=Page.objects.filter_active(), required=False)
2970

3071
class Meta:
3172
model = Section
32-
fields = COMMON_ADMIN_FIELDS + ("page", "order", "body_ko", "body_en")
73+
fields = COMMON_ADMIN_FIELDS + ("page", "external_link", "order", "body_ko", "body_en")
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Generated by Django 5.2 on 2025-06-21 12:11
2+
3+
import django.db.models.deletion
4+
from django.conf import settings
5+
from django.db import migrations, models
6+
7+
8+
class Migration(migrations.Migration):
9+
dependencies = [
10+
("cms", "0008_historicalsitemap_hide_sitemap_hide"),
11+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12+
]
13+
14+
operations = [
15+
migrations.AddField(
16+
model_name="historicalsitemap",
17+
name="external_link",
18+
field=models.URLField(
19+
blank=True, help_text="외부 링크인 경우 Page를 지정하는 대신 URL을 입력하세요.", null=True
20+
),
21+
),
22+
migrations.AddField(
23+
model_name="historicalsitemap",
24+
name="is_frontend_page",
25+
field=models.BooleanField(
26+
default=False, help_text="만약 이 Sitemap이 프론트엔드에 하드코딩된 페이지라면 체크하세요."
27+
),
28+
),
29+
migrations.AddField(
30+
model_name="sitemap",
31+
name="external_link",
32+
field=models.URLField(
33+
blank=True, help_text="외부 링크인 경우 Page를 지정하는 대신 URL을 입력하세요.", null=True
34+
),
35+
),
36+
migrations.AddField(
37+
model_name="sitemap",
38+
name="is_frontend_page",
39+
field=models.BooleanField(
40+
default=False, help_text="만약 이 Sitemap이 프론트엔드에 하드코딩된 페이지라면 체크하세요."
41+
),
42+
),
43+
migrations.AlterField(
44+
model_name="historicalsitemap",
45+
name="page",
46+
field=models.ForeignKey(
47+
blank=True,
48+
db_constraint=False,
49+
help_text="Sitemap 클릭 시 보여질 Page",
50+
null=True,
51+
on_delete=django.db.models.deletion.DO_NOTHING,
52+
related_name="+",
53+
to="cms.page",
54+
),
55+
),
56+
migrations.AlterField(
57+
model_name="sitemap",
58+
name="page",
59+
field=models.ForeignKey(
60+
blank=True,
61+
help_text="Sitemap 클릭 시 보여질 Page",
62+
null=True,
63+
on_delete=django.db.models.deletion.PROTECT,
64+
to="cms.page",
65+
),
66+
),
67+
migrations.AddConstraint(
68+
model_name="sitemap",
69+
constraint=models.UniqueConstraint(
70+
fields=("parent_sitemap", "route_code"), name="uq__sitemap__parent_route_code"
71+
),
72+
),
73+
]

app/cms/models.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,17 @@ class Sitemap(BaseAbstractModel):
8484
route_code = models.CharField(max_length=256, blank=True)
8585
name = models.CharField(max_length=256)
8686
order = models.IntegerField(default=0, validators=[MinValueValidator(0)])
87-
page = models.ForeignKey(Page, on_delete=models.PROTECT)
87+
88+
page = models.ForeignKey(
89+
Page, on_delete=models.PROTECT, null=True, blank=True, help_text="Sitemap 클릭 시 보여질 Page"
90+
)
91+
external_link = models.URLField(
92+
null=True, blank=True, help_text="외부 링크인 경우 Page를 지정하는 대신 URL을 입력하세요."
93+
)
94+
is_frontend_page = models.BooleanField(
95+
default=False, help_text="만약 이 Sitemap이 프론트엔드에 하드코딩된 페이지라면 체크하세요."
96+
)
97+
8898
hide = models.BooleanField(default=False, help_text="이 Sitemap을 숨길지 여부")
8999

90100
display_start_at = models.DateTimeField(null=True, blank=True)
@@ -94,6 +104,9 @@ class Sitemap(BaseAbstractModel):
94104

95105
class Meta:
96106
ordering = ["order"]
107+
constraints = [
108+
models.UniqueConstraint(fields=["parent_sitemap", "route_code"], name="uq__sitemap__parent_route_code"),
109+
]
97110

98111
def __str__(self):
99112
return f"{self.route} ({self.name})"

app/cms/serializers.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,15 @@
66
class SitemapSerializer(serializers.ModelSerializer):
77
class Meta:
88
model = Sitemap
9-
fields = COMMON_FIELDS + ("parent_sitemap", "route_code", "name", "order", "page", "hide")
9+
fields = COMMON_FIELDS + (
10+
"parent_sitemap",
11+
"route_code",
12+
"name",
13+
"order",
14+
"page",
15+
"external_link",
16+
"hide",
17+
)
1018

1119

1220
class SectionSerializer(serializers.ModelSerializer):

0 commit comments

Comments
 (0)