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: 5 additions & 3 deletions app/admin_api/serializers/modification_audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import unicodedata

from core.serializer.json_schema_serializer import JsonSchemaSerializer
from django.db import transaction
from event.presentation.models import Presentation, PresentationSpeaker
from participant_portal_api.models import ModificationAudit, ModificationAuditComment
from rest_framework import serializers
Expand Down Expand Up @@ -63,9 +64,10 @@ def validate(self, attrs: dict) -> dict:

def save(self, **kwargs: dict) -> ModificationAudit:
instance: ModificationAudit = self.instance
instance.status = ModificationAudit.Status.APPROVED
instance.apply_modification()
instance.save()
with transaction.atomic():
instance.status = ModificationAudit.Status.APPROVED
instance.apply_modification()
instance.save()

return instance

Expand Down
28 changes: 28 additions & 0 deletions app/core/test/django_orm_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import pytest
from core.util.django_orm import apply_diff_to_model, model_to_identifier
from django.utils.translation import override
from event.presentation.models import Presentation
from model_bakery import baker


@pytest.mark.parametrize("active_language", ["ko", "en"], ids=["applied_in_ko", "applied_in_en"])
def test_apply_diff_to_model_ignores_translated_original_field(db, active_language):
presentation = baker.make(Presentation, title_ko="한글제목", title_en="EnglishTitle")
diff = {model_to_identifier(presentation): {"title": "NewEnglishTitle", "title_en": "NewEnglishTitle"}}

with override(active_language):
apply_diff_to_model(diff)

presentation.refresh_from_db()
assert presentation.title_en == "NewEnglishTitle"
assert presentation.title_ko == "한글제목"


def test_apply_diff_to_model_applies_plain_field(db):
presentation = baker.make(Presentation, slideshow_url="https://example.com/old")
diff = {model_to_identifier(presentation): {"slideshow_url": "https://example.com/new"}}

apply_diff_to_model(diff)

presentation.refresh_from_db()
assert presentation.slideshow_url == "https://example.com/new"
11 changes: 11 additions & 0 deletions app/core/util/django_orm.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
from django.db.models.fields.files import FieldFile
from django.forms import model_to_dict
from django.utils.functional import Promise
from modeltranslation.translator import NotRegistered
from modeltranslation.translator import translator as modeltranslation_translator


def arbitrary_value_to_basic_type(value: typing.Any) -> str | int | float | bool | None:
Expand Down Expand Up @@ -258,8 +260,17 @@ def apply_diff_to_model(models_data: dict[str, dict[str, typing.Any]]) -> list[m
if not (model_instance := identifier_to_model(model_identifier)):
raise ValueError(f"Model class not found for identifier: {model_identifier}")

# 원본 필드는 활성 언어로 라우팅되는 descriptor라 적용 시점의 언어에 좌우되므로, 언어별 컬럼만 적용한다.
try:
skip_fields = set(modeltranslation_translator.get_options_for_model(type(model_instance)).fields)
except NotRegistered:
skip_fields = set()

# Apply the data to the model instance
for field_name, value in model_data.items():
if field_name in skip_fields:
continue

if is_identifier(value):
# If the value is a model identifier, resolve it to a model instance
if not (related_model_instance := identifier_to_model(value)):
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from contextlib import contextmanager
from types import SimpleNamespace

import pytest
from core.util.thread_local import thread_local
from django.utils.translation import override
from participant_portal_api.models import ModificationAudit
from participant_portal_api.serializers.user import UserPortalSerializer
from rest_framework import status
from rest_framework.test import APIClient
from user.models import UserExt

APPROVE_URL = "/v1/admin-api/participant_portal_api/modificationaudit/{audit_id}/approve/"


@contextmanager
def _acting_as(user: UserExt):
thread_local.current_request = SimpleNamespace(user=user)
try:
yield
finally:
if hasattr(thread_local, "current_request"):
del thread_local.current_request


@pytest.fixture
def requester(db) -> UserExt:
return UserExt.objects.create_user(
username="requester", email="requester@example.com", nickname_ko="한글이름", nickname_en="EnglishName"
)


@pytest.fixture
def superuser(db) -> UserExt:
return UserExt.objects.create_superuser(username="admin", email="admin@example.com")


@pytest.fixture
def admin_client(superuser) -> APIClient:
client = APIClient()
client.force_authenticate(user=superuser)
return client


def _request_profile_modification(requester: UserExt, payload: dict, language: str) -> ModificationAudit:
with override(language), _acting_as(requester):
serializer = UserPortalSerializer(instance=requester, data=payload, partial=True)
assert serializer.is_valid(), serializer.errors
serializer.save()

return ModificationAudit.objects.get(status=ModificationAudit.Status.REQUESTED)


def test_approve_en_nickname_request_under_ko_locale_keeps_ko_nickname(requester, admin_client):
audit = _request_profile_modification(requester, {"nickname_en": "NewEnglish"}, language="en")

response = admin_client.patch(
APPROVE_URL.format(audit_id=audit.id), data={}, format="json", HTTP_ACCEPT_LANGUAGE="ko"
)

assert response.status_code == status.HTTP_200_OK, response.data
requester.refresh_from_db()
assert requester.nickname_en == "NewEnglish"
assert requester.nickname_ko == "한글이름"


def test_approve_ko_nickname_request_under_en_locale_keeps_en_nickname(requester, admin_client):
audit = _request_profile_modification(requester, {"nickname_ko": "새한글이름"}, language="ko")

response = admin_client.patch(
APPROVE_URL.format(audit_id=audit.id), data={}, format="json", HTTP_ACCEPT_LANGUAGE="en"
)

assert response.status_code == status.HTTP_200_OK, response.data
requester.refresh_from_db()
assert requester.nickname_ko == "새한글이름"
assert requester.nickname_en == "EnglishName"
Loading