Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ local-makemigrations:
local-migrate:
@ENV_PATH=envfile/.env.local uv run python app/manage.py migrate

# Create admin superuser
local-createsuperuser:
@ENV_PATH=envfile/.env.local uv run python app/manage.py createsuperuser

# Devtools
hooks-install: local-setup
uv run pre-commit install
Expand Down
65 changes: 63 additions & 2 deletions app/cms/admin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from cms.admin_mixins import RelatedReadonlyFieldsMixin
from cms.models import Page, Section, Sitemap
from django import forms
from django.contrib import admin
Expand Down Expand Up @@ -149,10 +150,70 @@ class Meta:
widgets = {"body": CodeEditorWidget()}


@admin.register(Sitemap)
class SitemapAdmin(RelatedReadonlyFieldsMixin, admin.ModelAdmin):
fields = ["id", "parent_sitemap", "page", "name", "order", "display_start_at", "display_end_at"]
readonly_fields = ["id"]
related_readonly_config = {
"page": ["id", "is_active", "css", "title", "subtitle"],
"parent_sitemap": ["id", "name", "order", "display_start_at", "display_end_at"],
}

def get_fieldsets(self, request, obj=...):
original_fieldsets = super().get_fieldsets(request, obj)
if obj and obj.parent_sitemap:
original_fieldsets.append(
(
"Parent Sitemap 정보",
{
"fields": [f"get_parent_sitemap_{f}" for f in self.related_readonly_config["parent_sitemap"]],
"classes": ("collapse",),
},
)
)
if obj and obj.page:
original_fieldsets.append(
(
"Page 정보",
{
"fields": [f"get_page_{f}" for f in self.related_readonly_config["page"]],
"classes": ("collapse",),
},
)
)
return original_fieldsets

def get_queryset(self, request):
return super().get_queryset(request).select_related("page").select_related("parent_sitemap")


class PageAdmin(admin.ModelAdmin):
pass


@admin.register(Section)
class SectionAdmin(admin.ModelAdmin):
class SectionAdmin(RelatedReadonlyFieldsMixin, admin.ModelAdmin):
form = SectionAdminForm
fields = ["id", "page", "order", "css", "body"]
readonly_fields = ["id"]
related_readonly_config = {"page": ["id", "is_active", "css", "title", "subtitle"]}

def get_fieldsets(self, request, obj=...):
original_fieldsets = super().get_fieldsets(request, obj)
if obj and obj.page:
original_fieldsets.append(
(
"Page 정보",
{
"fields": [f"get_page_{f}" for f in self.related_readonly_config["page"]],
"classes": ("collapse",),
},
)
)
return original_fieldsets

def get_queryset(self, request):
return super().get_queryset(request).select_related("page")


admin.site.register(Page)
admin.site.register(Sitemap)
26 changes: 26 additions & 0 deletions app/cms/admin_mixins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
class RelatedReadonlyFieldsMixin:
related_readonly_config = {}

def _generate_related_getter(self, rel, field, prefix=""):
def _func(admin_self, obj):
related = getattr(obj, rel)
return getattr(related, field) if related else None

_func.short_description = f"{prefix} {field.replace('_', ' ')}"
return _func

def _register_dynamic_readonly_fields(self):
for rel, fields in self.related_readonly_config.items():
for field in fields:
method_name = f"get_{rel}_{field}"
getter = self._generate_related_getter(rel, field, prefix=rel.capitalize())
setattr(self.__class__, method_name, getter)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._register_dynamic_readonly_fields()

def get_readonly_fields(self, request, obj=None):
base = super().get_readonly_fields(request, obj)
generated = [f"get_{rel}_{field}" for rel, fields in self.related_readonly_config.items() for field in fields]
return list(base) + generated
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 5.2 on 2025-05-07 11:52

import django.core.validators
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("cms", "0002_historicalpage_historicalsection_historicalsitemap"),
]

operations = [
migrations.AlterField(
model_name="historicalsitemap",
name="order",
field=models.IntegerField(default=0, validators=[django.core.validators.MinValueValidator(0)]),
),
migrations.AlterField(
model_name="sitemap",
name="order",
field=models.IntegerField(default=0, validators=[django.core.validators.MinValueValidator(0)]),
),
]
3 changes: 2 additions & 1 deletion app/cms/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import uuid

from django.core.validators import MinValueValidator
from django.db import models


Expand All @@ -21,7 +22,7 @@ class Sitemap(models.Model):
"self", null=True, blank=True, default=None, on_delete=models.SET_NULL, related_name="children"
)
name = models.CharField(max_length=256)
order = models.IntegerField(default=0)
order = models.IntegerField(default=0, validators=[MinValueValidator(0)])
page = models.ForeignKey(Page, on_delete=models.PROTECT)
display_start_at = models.DateTimeField(null=True, blank=True)
display_end_at = models.DateTimeField(null=True, blank=True)
Expand Down
4 changes: 2 additions & 2 deletions app/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,13 +308,13 @@
SESSION_COOKIE_SAMESITE = COOKIE_SAMESITE
SESSION_COOKIE_SECURE = COOKIE_SECURE
SESSION_COOKIE_HTTPONLY = COOKIE_HTTPONLY
SESSION_COOKIE_DOMAIN = COOKIE_DOMAIN
SESSION_COOKIE_DOMAIN = None if IS_LOCAL else COOKIE_DOMAIN

CSRF_COOKIE_NAME = f"{COOKIE_PREFIX}csrftoken"
CSRF_COOKIE_SAMESITE = COOKIE_SAMESITE
CSRF_COOKIE_SECURE = COOKIE_SECURE
CSRF_COOKIE_HTTPONLY = COOKIE_HTTPONLY
CSRF_COOKIE_DOMAIN = COOKIE_DOMAIN
CSRF_COOKIE_DOMAIN = None if IS_LOCAL else COOKIE_DOMAIN
CSRF_TRUSTED_ORIGINS = set(env.list("CSRF_TRUSTED_ORIGINS", default=["https://pycon.kr"])) | {
"https://local.dev.pycon.kr:3000",
"https://localhost:3000",
Expand Down