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
8 changes: 6 additions & 2 deletions src/activity_gallery/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,11 @@ def image_preview(self, obj):
class ActivityActionAdmin(admin.ModelAdmin):
"""์ปค๋ฎค๋‹ˆํ‹ฐ ํ™œ๋™ ๊ด€๋ฆฌ์ž ํŽ˜์ด์ง€"""

list_display = ("id", "title", "content_preview")
list_display = ("id", "title", "content_preview", "is_visible")
list_editable = ("is_visible",)
search_fields = ("title", "content")
inlines = [ActivityTagInline, ActionPhotoInline] # ํƒœ๊ทธ & ์‚ฌ์ง„ ์ถ”๊ฐ€ ๊ฐ€๋Šฅ
exclude = ("is_deleted",)

def content_preview(self, obj):
"""๋‚ด์šฉ์ด ๊ธธ ๊ฒฝ์šฐ ์ผ๋ถ€๋งŒ ๋ฏธ๋ฆฌ๋ณด๊ธฐ"""
Expand All @@ -69,8 +71,10 @@ def delete_queryset(self, request, queryset):
class ActivityHistoryAdmin(admin.ModelAdmin):
"""ํ™œ๋™ ํžˆ์Šคํ† ๋ฆฌ ๊ด€๋ฆฌ์ž ํŽ˜์ด์ง€"""

list_display = ("title", "activity_date")
list_display = ("title", "activity_date", "is_visible")
list_editable = ("is_visible",)
search_fields = ("title", "content", "activity_date")
exclude = ("is_deleted",)

def content_preview(self, obj):
"""๋‚ด์šฉ์ด ๊ธธ ๊ฒฝ์šฐ ์ผ๋ถ€๋งŒ ๋ฏธ๋ฆฌ๋ณด๊ธฐ"""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Generated by Django 5.2.3 on 2025-07-08 15:29

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("activity_gallery", "0008_activityhistory"),
]

operations = [
migrations.AddField(
model_name="activityaction",
name="is_deleted",
field=models.BooleanField(default=False, verbose_name="์ˆจ๊น€ ์—ฌ๋ถ€"),
),
migrations.AddField(
model_name="activityaction",
name="is_visible",
field=models.BooleanField(default=True, verbose_name="ํ™œ๋™ ๊ณต๊ฐœ ์—ฌ๋ถ€"),
),
migrations.AddField(
model_name="activityhistory",
name="is_visible",
field=models.BooleanField(default=True, verbose_name="ํžˆ์Šคํ† ๋ฆฌ ๊ณต๊ฐœ ์—ฌ๋ถ€"),
),
migrations.AlterField(
model_name="actionphoto",
name="image",
field=models.ImageField(upload_to="activity/action-photo/", verbose_name="ํ™œ๋™ ์‚ฌ์ง„"),
),
migrations.AlterField(
model_name="activityaction",
name="content",
field=models.TextField(verbose_name="์ปค๋ฎค๋‹ˆํ‹ฐ ํ™œ๋™ ๋‚ด์šฉ"),
),
migrations.AlterField(
model_name="activityhistory",
name="content",
field=models.TextField(verbose_name="ํžˆ์Šคํ† ๋ฆฌ ๋‚ด์šฉ"),
),
migrations.AlterField(
model_name="activityhistory",
name="created_at",
field=models.DateTimeField(auto_now_add=True, verbose_name="์ƒ์„ฑ์ผ์‹œ"),
),
migrations.AlterField(
model_name="activityhistory",
name="thumbnail",
field=models.ImageField(
blank=True,
null=True,
upload_to="activity/history/thumbnail/",
verbose_name="ํžˆ์Šคํ† ๋ฆฌ ์ธ๋„ค์ผ",
),
),
migrations.AlterField(
model_name="activityhistory",
name="title",
field=models.CharField(max_length=255, verbose_name="ํžˆ์Šคํ† ๋ฆฌ ์ด๋ฆ„"),
),
migrations.AlterField(
model_name="activityhistory",
name="updated_at",
field=models.DateTimeField(auto_now=True, verbose_name="์ˆ˜์ •์ผ์‹œ"),
),
]
20 changes: 11 additions & 9 deletions src/activity_gallery/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@
from public.tag_models import ActivityTag, Tag


class ActivityAction(MultiImageFieldMixin):
class ActivityAction(MultiImageFieldMixin, SoftDeleteModel):
"""
### ์ปค๋ฎค๋‹ˆํ‹ฐ ํ™œ๋™ ๋ชจ๋ธ
"""

title = models.CharField(max_length=20, verbose_name="ํ™œ๋™๋ช…")
thumbnail = models.ImageField("์ธ๋„ค์ผ ์ด๋ฏธ์ง€", upload_to="activity/thumbnail/")
content = models.TextField(verbose_name="๋‚ด์šฉ")
content = models.TextField("์ปค๋ฎค๋‹ˆํ‹ฐ ํ™œ๋™ ๋‚ด์šฉ")
tags = models.ManyToManyField(Tag, through=ActivityTag, related_name="actions")
is_visible = models.BooleanField("ํ™œ๋™ ๊ณต๊ฐœ ์—ฌ๋ถ€", default=True)

# MultiImageFieldMixin์—์„œ clean, delete๋ฅผ ์œ„ํ•ด ํ•„์š”ํ•œ ์ •๋ณด ์ž‘์„ฑ
image_field_names = ["thumbnail"]
Expand All @@ -33,7 +34,7 @@ class ActionPhoto(MultiImageFieldMixin):
activity_action = models.ForeignKey(
ActivityAction, on_delete=models.CASCADE, related_name="photos", null=True, blank=True
)
image = models.ImageField(upload_to="activity/action-photo/")
image = models.ImageField("ํ™œ๋™ ์‚ฌ์ง„", upload_to="activity/action-photo/")

# MultiImageFieldMixin์—์„œ clean, delete๋ฅผ ์œ„ํ•ด ํ•„์š”ํ•œ ์ •๋ณด ์ž‘์„ฑ
image_field_names = ["image"]
Expand All @@ -43,14 +44,15 @@ def __str__(self):


class ActivityHistory(SoftDeleteModel):
title = models.CharField(max_length=255, verbose_name="ํ™œ๋™๋ช…")
content = models.TextField(verbose_name="๋‚ด์šฉ")
title = models.CharField("ํžˆ์Šคํ† ๋ฆฌ ์ด๋ฆ„", max_length=255)
content = models.TextField("ํžˆ์Šคํ† ๋ฆฌ ๋‚ด์šฉ")
thumbnail = models.ImageField(
"ํ™œ๋™ ์ธ๋„ค์ผ ์ด๋ฏธ์ง€", upload_to="activity/history/thumbnail/", null=True, blank=True
"ํžˆ์Šคํ† ๋ฆฌ ์ธ๋„ค์ผ", upload_to="activity/history/thumbnail/", null=True, blank=True
)
activity_date = models.DateField(verbose_name="ํ™œ๋™ ๋‚ ์งœ")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="์ƒ์„ฑ์ผ")
updated_at = models.DateTimeField(auto_now=True, verbose_name="์ˆ˜์ •์ผ")
activity_date = models.DateField("ํ™œ๋™ ๋‚ ์งœ")
is_visible = models.BooleanField("ํžˆ์Šคํ† ๋ฆฌ ๊ณต๊ฐœ ์—ฌ๋ถ€", default=True)
created_at = models.DateTimeField("์ƒ์„ฑ์ผ์‹œ", auto_now_add=True)
updated_at = models.DateTimeField("์ˆ˜์ •์ผ์‹œ", auto_now=True)

class Meta:
verbose_name = "ํ™œ๋™ ํžˆ์Šคํ† ๋ฆฌ"
Expand Down
2 changes: 1 addition & 1 deletion src/activity_gallery/tests/test_activity_history_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,5 +170,5 @@ def test_delete_activity_history_when_soft_delete_operation() -> None:
history.delete()

# Then
deleted = ActivityHistory.objects.get(id=history.id)
deleted = ActivityHistory.all_objects.get(id=history.id)
assert deleted.is_deleted is True
8 changes: 6 additions & 2 deletions src/activity_gallery/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ class ActivityActionViewSet(GenericViewSet, ListModelMixin, RetrieveModelMixin):

def get_queryset(self):
"""๊ณตํ†ต ์ฟผ๋ฆฌ์…‹"""
return ActivityAction.objects.prefetch_related("photos", "activity_tags__tag").all()
return ActivityAction.objects.prefetch_related("photos", "activity_tags__tag").filter(
is_visible=True
)

def get_serializer_class(self):
"""์š”์ฒญ ๋ฐฉ์‹์— ๋”ฐ๋ผ ์ ์ ˆํ•œ Serializer ๋ฐ˜ํ™˜"""
Expand All @@ -49,9 +51,11 @@ def retrieve(self, request, *args, **kwargs) -> BaseResponse:
class ActivityHistoryViewSet(GenericViewSet, ListModelMixin):
"""ํ™œ๋™ ํžˆ์Šคํ† ๋ฆฌ API ๋ทฐ"""

queryset = ActivityHistory.objects.filter(is_deleted=False).all()
serializer_class = ActivityHistorySerializer

def get_queryset(self):
return ActivityHistory.objects.filter(is_visible=True).all()

def list(self, request, *args, **kwargs) -> BaseResponse:
"""ํ™œ๋™ ํžˆ์Šคํ† ๋ฆฌ ๋ชฉ๋ก ์กฐํšŒ API"""
queryset = self.get_queryset()
Expand Down
19 changes: 18 additions & 1 deletion src/core/mixins/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,30 @@
from django.db import models


class SoftDeleteObjectManager(models.Manager):
"""
Custom manager to filter out soft-deleted objects by default.
"""

def get_queryset(self):
return super().get_queryset().filter(is_deleted=False)

def all_with_deleted(self):
return super().get_queryset() # Returns all objects, including soft-deleted ones


class SoftDeleteModel(models.Model):
is_deleted = models.BooleanField("์ˆจ๊น€ ์—ฌ๋ถ€", default=False)

objects = SoftDeleteObjectManager()
all_objects = models.Manager() # Manager to access all objects, including soft-deleted

class Meta:
abstract = True

def delete(self, force_delete: bool = False, using: Any = None, keep_parents: bool = False):
def delete(
self, using: Any = None, keep_parents: bool = False, force_delete: bool = False
) -> None:
if force_delete:
return super().delete(using=using, keep_parents=keep_parents)
self.is_deleted = True
Expand Down
4 changes: 3 additions & 1 deletion src/faq/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@

@admin.register(FAQ)
class FAQAdmin(admin.ModelAdmin):
list_display = ("question", "answer")
list_display = ("question", "answer", "is_visible")
list_editable = ("is_visible",)
exclude = ("is_deleted",)
25 changes: 25 additions & 0 deletions src/faq/migrations/0008_alter_faq_options_faq_is_visible.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 5.2.3 on 2025-07-08 15:29

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("faq", "0007_alter_faq_options"),
]

operations = [
migrations.AlterModelOptions(
name="faq",
options={
"ordering": ["-created_at"],
"verbose_name": "FAQ",
"verbose_name_plural": "FAQs",
},
),
migrations.AddField(
model_name="faq",
name="is_visible",
field=models.BooleanField(default=True, verbose_name="๊ณต๊ฐœ ์—ฌ๋ถ€"),
),
]
7 changes: 4 additions & 3 deletions src/faq/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ class FAQ(SoftDeleteModel):
### FAQ ํ•„๋“œ ์ •์˜
"""

question = models.CharField(max_length=255, verbose_name="์งˆ๋ฌธ")
answer = models.TextField(verbose_name="๋‹ต๋ณ€")
question = models.CharField("์งˆ๋ฌธ", max_length=255)
answer = models.TextField("๋‹ต๋ณ€")
is_visible = models.BooleanField("๊ณต๊ฐœ ์—ฌ๋ถ€", default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

class Meta:
verbose_name = "FAQ"
verbose_name_plural = "์ž์ฃผํ•˜๋Š” ์งˆ๋ฌธ"
verbose_name_plural = "FAQs"
ordering = ["-created_at"]

def __str__(self):
Expand Down
2 changes: 1 addition & 1 deletion src/faq/tests/test_faq_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ def test_delete_faq_when_soft_delete_operation(
faq.delete()

# Then
deleted = FAQ.objects.get(id=faq_id)
deleted = FAQ.all_objects.get(id=faq_id)
assert deleted.is_deleted is True


Expand Down
2 changes: 1 addition & 1 deletion src/faq/tests/test_faq_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def test_faq_serializer_is_valid_false_given_invalid_data(
(
"What is the best way to learn Python?",
"The best way to learn Python is to practice coding every day.",
True,
False,
),
],
)
Expand Down
5 changes: 4 additions & 1 deletion src/merchandise/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@

@admin.register(Merchandise)
class MerchandiseAdmin(admin.ModelAdmin):
list_display = ("name", "image")
list_display = ("name", "image", "is_visible")
list_editable = ("is_visible",)
search_fields = ("name", "description")
exclude = ("is_deleted",)

def delete_queryset(self, request, queryset):
# Admin์—์„œ ์—ฌ๋Ÿฌ ๊ฐ์ฒด ์‚ญ์ œ ์‹œ ํ˜ธ์ถœ๋จ
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 5.2.3 on 2025-07-08 15:53

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("merchandise", "0005_alter_merchandise_image"),
]

operations = [
migrations.AddField(
model_name="merchandise",
name="is_deleted",
field=models.BooleanField(default=False, verbose_name="์ˆจ๊น€ ์—ฌ๋ถ€"),
),
migrations.AddField(
model_name="merchandise",
name="is_visible",
field=models.BooleanField(default=True, verbose_name="์ƒํ’ˆ ๊ณต๊ฐœ ์—ฌ๋ถ€"),
),
]
4 changes: 3 additions & 1 deletion src/merchandise/models.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
from django.db import models

from core.mixins.models import SoftDeleteModel
from public.mixin.img_models import MultiImageFieldMixin


class Merchandise(MultiImageFieldMixin):
class Merchandise(MultiImageFieldMixin, SoftDeleteModel):
"""
### MD ๋ชจ๋ธ
"""

name = models.CharField("์ƒํ’ˆ ์ด๋ฆ„", max_length=255)
description = models.TextField("์ƒํ’ˆ ์„ค๋ช…", null=True, blank=True)
is_visible = models.BooleanField("์ƒํ’ˆ ๊ณต๊ฐœ ์—ฌ๋ถ€", default=True)
image = models.ImageField("์ธ๋„ค์ผ ์ด๋ฏธ์ง€", upload_to="merchandise/image/")

# MultiImageFieldMixin์—์„œ clean, delete๋ฅผ ์œ„ํ•ด ํ•„์š”ํ•œ ์ •๋ณด ์ž‘์„ฑ
Expand Down
7 changes: 4 additions & 3 deletions src/notice/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ def save_new_instance(self, obj, *args, **kwargs):

@admin.register(Notice)
class NoticeAdmin(admin.ModelAdmin):
list_display = ("id", "title", "is_pinned", "is_deleted")
list_filter = ("is_pinned",)
list_display = ("id", "title", "is_pinned", "is_visible")
list_filter = ("is_pinned", "is_visible")
exclude = ("is_deleted",)
list_editable = ("is_visible", "is_pinned")
search_fields = ("title", "content")
ordering = ("-created_at",)

fieldsets = (("์ˆ˜์ • ๊ฐ€๋Šฅ ํ•„๋“œ", {"fields": ("title", "content", "is_pinned")}),)
inlines = [NoticeTagInline]

def delete_queryset(self, request, queryset):
Expand Down
17 changes: 17 additions & 0 deletions src/notice/migrations/0007_notice_is_visible.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 5.2.3 on 2025-07-08 15:29

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("notice", "0006_rename_tag_notice_tags"),
]

operations = [
migrations.AddField(
model_name="notice",
name="is_visible",
field=models.BooleanField(default=True, verbose_name="๊ณต์ง€์‚ฌํ•ญ ๊ณต๊ฐœ ์—ฌ๋ถ€"),
),
]
1 change: 1 addition & 0 deletions src/notice/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class Notice(SoftDeleteModel):
title = models.CharField("์ œ๋ชฉ", max_length=255)
content = CKEditor5Field("๋ณธ๋ฌธ", config_name="extends", null=False, blank=False)
is_pinned = models.BooleanField("์ƒ๋‹จ ๊ณ ์ •", default=False)
is_visible = models.BooleanField("๊ณต์ง€์‚ฌํ•ญ ๊ณต๊ฐœ ์—ฌ๋ถ€", default=True)
created_at = models.DateTimeField("๋“ฑ๋ก ์ผ์‹œ", auto_now_add=True)
updated_at = models.DateTimeField("์ˆ˜์ • ์ผ์‹œ", auto_now=True)
tags = models.ManyToManyField(Tag, through=NoticeTag, related_name="notice")
Expand Down
2 changes: 1 addition & 1 deletion src/notice/tests/test_notice_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ def test_delete_notice_when_soft_delete_operation(
notice.delete()

# Then
deleted = Notice.objects.get(id=notice_id)
deleted = Notice.all_objects.get(id=notice_id)
assert deleted.is_deleted is True


Expand Down