Skip to content

Commit b441bd7

Browse files
committed
fix: ModificationAudit이 one to many / many to many도 대응하도록 수정
1 parent 6a37fd3 commit b441bd7

File tree

2 files changed

+71
-27
lines changed

2 files changed

+71
-27
lines changed

app/participant_portal_api/models.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -61,23 +61,35 @@ def __str__(self) -> str:
6161
def apply_modification(self, save: bool = False) -> models.Model:
6262
for field, value in self.modification_data.items():
6363
if isinstance(value, list):
64-
if not (sub_qs := getattr(self.instance, field, None)):
64+
# One to Many case
65+
sub_value_map = {sub_value["id"]: sub_value for sub_value in value}
66+
if not (sub_instances := list(getattr(self.instance, field).filter(pk__in=sub_value_map))):
6567
continue
66-
if not isinstance(sub_qs, models.manager.BaseManager):
67-
continue
68-
for sub_value in value:
69-
if not isinstance(sub_value, dict):
70-
continue
71-
if not (sub_instance_id := sub_value.get("id")):
72-
continue
73-
sub_qs.filter(pk=sub_instance_id).update(**sub_value)
68+
69+
for sub_instance in sub_instances:
70+
sub_data = sub_value_map.get[str(sub_instance.pk)]
71+
for sub_field, sub_value in sub_data.items():
72+
setattr(sub_instance, sub_field, sub_value)
73+
74+
if save:
75+
sub_instance.save()
76+
77+
if not save:
78+
setattr(self.instance, field, sub_instances)
7479
elif isinstance(value, dict):
80+
# One to One case
7581
if not (sub_instance := getattr(self.instance, field, None)):
7682
continue
83+
7784
for sub_field, sub_value in value.items():
7885
setattr(sub_instance, sub_field, sub_value)
79-
sub_instance.save()
86+
87+
if save:
88+
sub_instance.save()
89+
else:
90+
setattr(self.instance, field, sub_instance)
8091
else:
92+
# 일반 필드 업데이트
8193
setattr(self.instance, field, value)
8294

8395
if save:

app/participant_portal_api/serializers/modification_audit.py

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,51 @@ class Meta:
4141
)
4242

4343

44+
def _model_to_jsonable_dict(instance: models.Model, new_data: dict) -> dict[str, str | None]:
45+
"""모델 인스턴스를 JSON 직렬화 가능한 딕셔너리로 변환합니다."""
46+
if not instance:
47+
return {}
48+
49+
data: dict[str, str | None] = {}
50+
for field, value in new_data.items():
51+
if not hasattr(instance, field):
52+
continue
53+
54+
if isinstance(value, list):
55+
sub_data = []
56+
57+
for sub_value in value:
58+
# One to Many case
59+
if not isinstance(sub_value, dict) or "id" not in sub_value:
60+
continue
61+
sub_qs = getattr(instance, field, None)
62+
if not isinstance(sub_qs, models.manager.BaseManager):
63+
continue
64+
if not (sub_instance := sub_qs.filter(pk=sub_value["id"]).first()):
65+
continue
66+
67+
sub_data.append(_model_to_jsonable_dict(sub_instance, sub_value))
68+
69+
if sub_data:
70+
data[field] = sub_data
71+
72+
elif isinstance(value, dict):
73+
# One to One case
74+
if not (sub_instance := getattr(instance, field, None)):
75+
continue
76+
77+
data[field] = _model_to_jsonable_dict(sub_instance, value)
78+
79+
elif getattr(instance, field) != value:
80+
if isinstance(value, models.Model):
81+
field = f"{getattr(instance._meta.model, field).field.name}_id"
82+
value = str(value.pk)
83+
84+
data[field] = value
85+
86+
return data
87+
88+
4489
class ModificationAuditCreationPortalSerializer(serializers.ModelSerializer):
4590
has_requested_modification_audit = serializers.SerializerMethodField()
4691
requested_modification_audit_id = serializers.SerializerMethodField()
@@ -76,29 +121,16 @@ def validate(self, attrs: dict) -> dict:
76121
"Please cancel the existing request and try again."
77122
)
78123

79-
modification_data: dict[str, str | None] = {}
80-
for field, value in attrs.items():
81-
if hasattr(self.instance, field) and getattr(self.instance, field) != value:
82-
if isinstance(value, models.Model):
83-
field = f"{getattr(self.instance._meta.model, field).field.name}_id"
84-
value = str(value.pk)
85-
86-
modification_data[field] = value
87-
88-
if not modification_data:
124+
if not (modification_data := _model_to_jsonable_dict(self.instance, attrs)):
89125
raise serializers.ValidationError("변경된 데이터가 없습니다.\nNo modification data provided.")
90126

91127
return modification_data
92128

93129
def save(self, **kwargs: dict) -> models.Model:
94130
"""instance.save()를 호출하는 대신, 변경된 데이터를 추출하여 ModificationAudit 인스턴스를 생성합니다."""
95-
ModificationAudit.objects.create(instance=self.instance, modification_data=self.validated_data)
96-
97-
for field, value in self.validated_data.items():
98-
if hasattr(self.instance, field):
99-
setattr(self.instance, field, value)
100-
101-
return self.instance
131+
return ModificationAudit.objects.create(
132+
instance=self.instance, modification_data=self.validated_data
133+
).apply_modification(save=False)
102134

103135

104136
class ModificationAuditCancelPortalSerializer(serializers.ModelSerializer):

0 commit comments

Comments
 (0)