Skip to content

Commit 1481899

Browse files
committed
M2M handling in 3 DREF types
1 parent 859d5d7 commit 1481899

File tree

1 file changed

+234
-3
lines changed

1 file changed

+234
-3
lines changed

dref/serializers.py

Lines changed: 234 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import datetime
22
import os
3+
from ast import literal_eval as ev
34
from typing import List, Optional
45

56
from django.conf import settings
@@ -288,7 +289,12 @@ def create(self, validated_data):
288289
indicators = validated_data.pop("indicators", [])
289290
intervention = super().create(validated_data)
290291
for indicator in indicators:
291-
ind_object = PlannedInterventionIndicators.objects.create(**indicator)
292+
# Instead of ind_object = PlannedInterventionIndicators.objects.create(**indicator):
293+
ind_id = indicator.get("id")
294+
if ind_id:
295+
ind_object = PlannedInterventionIndicators.objects.get(id=ind_id)
296+
else:
297+
ind_object = PlannedInterventionIndicators.objects.create(**indicator)
292298
intervention.indicators.add(ind_object)
293299
return intervention
294300

@@ -691,6 +697,83 @@ def update(self, instance, validated_data):
691697
raise serializers.ValidationError({"modified_at": settings.DREF_OP_UPDATE_FINAL_REPORT_UPDATE_ERROR_MESSAGE})
692698
validated_data["modified_at"] = timezone.now()
693699
dref = super().update(instance, validated_data)
700+
701+
# Helpers for robust M2M update
702+
703+
def create_with_m2m(item, model_cls):
704+
# Only handle PlannedIntervention/indicators for now
705+
m2m_fields = []
706+
if model_cls.__name__ == "PlannedIntervention" and "indicators" in item:
707+
m2m_fields = ["indicators"]
708+
# Remove m2m fields from item for create
709+
item_clean = {k: v for k, v in item.items() if k not in m2m_fields}
710+
pk = item_clean.pop("pk")
711+
obj, created = model_cls.objects.get_or_create(pk=pk, defaults=item_clean)
712+
if not created:
713+
print("Updated existing PlannedIntervention object:", obj)
714+
715+
# Set m2m fields after create
716+
for m2m_field in m2m_fields:
717+
values = item[m2m_field]
718+
rel_model = obj._meta.get_field(m2m_field).related_model
719+
rel_ids = []
720+
for v in values:
721+
if isinstance(v, dict):
722+
rel_obj = rel_model.objects.create(**v)
723+
rel_ids.append(rel_obj.id)
724+
else:
725+
rel_ids.append(v)
726+
getattr(obj, m2m_field).set(rel_ids)
727+
return obj
728+
729+
def m2m(data, model_cls):
730+
ids = []
731+
for item in data:
732+
if isinstance(item, dict):
733+
item_id = item.get("id")
734+
if item_id:
735+
ids.append(item_id)
736+
else:
737+
obj = create_with_m2m(item, model_cls)
738+
ids.append(obj.id)
739+
else:
740+
ids.append(item)
741+
return ids
742+
743+
# planned_interventions M2M handling
744+
planned_interventions_data = self.initial_data.get("planned_interventions")
745+
if planned_interventions_data:
746+
dref.planned_interventions.set(m2m(planned_interventions_data, PlannedIntervention))
747+
748+
# needs_identified M2M handling
749+
needs_identified_data = self.initial_data.get("needs_identified")
750+
if needs_identified_data:
751+
dref.needs_identified.set(m2m(needs_identified_data, IdentifiedNeed))
752+
753+
# national_society_actions M2M handling
754+
national_society_actions_data = self.initial_data.get("national_society_actions")
755+
if national_society_actions_data:
756+
dref.national_society_actions.set(m2m(national_society_actions_data, NationalSocietyAction))
757+
758+
# risk_security M2M handling
759+
risk_security_data = self.initial_data.get("risk_security")
760+
if risk_security_data:
761+
dref.risk_security.set(m2m(risk_security_data, RiskSecurity))
762+
763+
# source_information M2M handling
764+
source_information_data = self.initial_data.get("source_information")
765+
if source_information_data:
766+
dref.source_information.set(m2m(source_information_data, SourceInformation))
767+
768+
# proposed_action M2M handling
769+
proposed_action_data = self.initial_data.get("proposed_action")
770+
if proposed_action_data:
771+
if isinstance(proposed_action_data, str):
772+
proposed_action_data = ev(proposed_action_data)
773+
if isinstance(proposed_action_data, dict):
774+
proposed_action_data = [proposed_action_data]
775+
dref.proposed_action.set(m2m(proposed_action_data, ProposedAction))
776+
694777
if to:
695778
transaction.on_commit(lambda: send_dref_email.delay(dref.id, list(to), "Updated"))
696779
return dref
@@ -1079,7 +1162,81 @@ def update(self, instance, validated_data):
10791162
if modified_at and instance.modified_at and modified_at < instance.modified_at:
10801163
raise serializers.ValidationError({"modified_at": settings.DREF_OP_UPDATE_FINAL_REPORT_UPDATE_ERROR_MESSAGE})
10811164
validated_data["modified_at"] = timezone.now()
1082-
return super().update(instance, validated_data)
1165+
operational_update = super().update(instance, validated_data)
1166+
1167+
# Helpers for robust M2M update
1168+
1169+
def create_with_m2m(item, model_cls):
1170+
# Only handle PlannedIntervention/indicators for now
1171+
m2m_fields = []
1172+
if model_cls.__name__ == "PlannedIntervention" and "indicators" in item:
1173+
m2m_fields = ["indicators"]
1174+
# Remove m2m fields from item for create
1175+
item_clean = {k: v for k, v in item.items() if k not in m2m_fields}
1176+
obj = model_cls.objects.create(**item_clean)
1177+
# Set m2m fields after create
1178+
for m2m_field in m2m_fields:
1179+
values = item[m2m_field]
1180+
rel_model = obj._meta.get_field(m2m_field).related_model
1181+
rel_ids = []
1182+
for v in values:
1183+
if isinstance(v, dict):
1184+
rel_obj = rel_model.objects.create(**v)
1185+
rel_ids.append(rel_obj.id)
1186+
else:
1187+
rel_ids.append(v)
1188+
getattr(obj, m2m_field).set(rel_ids)
1189+
return obj
1190+
1191+
def m2m(data, model_cls):
1192+
ids = []
1193+
for item in data:
1194+
if isinstance(item, dict):
1195+
item_id = item.get("id")
1196+
if item_id:
1197+
ids.append(item_id)
1198+
else:
1199+
obj = create_with_m2m(item, model_cls)
1200+
ids.append(obj.id)
1201+
else:
1202+
ids.append(item)
1203+
return ids
1204+
1205+
# planned_interventions M2M handling
1206+
planned_interventions_data = self.initial_data.get("planned_interventions")
1207+
if planned_interventions_data:
1208+
operational_update.planned_interventions.set(m2m(planned_interventions_data, PlannedIntervention))
1209+
1210+
# needs_identified M2M handling
1211+
needs_identified_data = self.initial_data.get("needs_identified")
1212+
if needs_identified_data:
1213+
operational_update.needs_identified.set(m2m(needs_identified_data, IdentifiedNeed))
1214+
1215+
# national_society_actions M2M handling
1216+
national_society_actions_data = self.initial_data.get("national_society_actions")
1217+
if national_society_actions_data:
1218+
operational_update.national_society_actions.set(m2m(national_society_actions_data, NationalSocietyAction))
1219+
1220+
# risk_security M2M handling
1221+
risk_security_data = self.initial_data.get("risk_security")
1222+
if risk_security_data:
1223+
operational_update.risk_security.set(m2m(risk_security_data, RiskSecurity))
1224+
1225+
# source_information M2M handling
1226+
source_information_data = self.initial_data.get("source_information")
1227+
if source_information_data:
1228+
operational_update.source_information.set(m2m(source_information_data, SourceInformation))
1229+
1230+
# proposed_action M2M handling
1231+
proposed_action_data = self.initial_data.get("proposed_action")
1232+
if proposed_action_data:
1233+
if isinstance(proposed_action_data, str):
1234+
proposed_action_data = ev(proposed_action_data)
1235+
if isinstance(proposed_action_data, dict):
1236+
proposed_action_data = [proposed_action_data]
1237+
operational_update.proposed_action.set(m2m(proposed_action_data, ProposedAction))
1238+
1239+
return operational_update
10831240

10841241

10851242
class DrefFinalReportSerializer(NestedUpdateMixin, NestedCreateMixin, ModelSerializer):
@@ -1520,7 +1677,81 @@ def update(self, instance, validated_data):
15201677
raise serializers.ValidationError({"modified_at": settings.DREF_OP_UPDATE_FINAL_REPORT_UPDATE_ERROR_MESSAGE})
15211678
validated_data["modified_at"] = timezone.now()
15221679
validated_data["modified_by"] = self.context["request"].user
1523-
return super().update(instance, validated_data)
1680+
final_report = super().update(instance, validated_data)
1681+
1682+
# Helpers for robust M2M update
1683+
1684+
def create_with_m2m(item, model_cls):
1685+
# Only handle PlannedIntervention/indicators for now
1686+
m2m_fields = []
1687+
if model_cls.__name__ == "PlannedIntervention" and "indicators" in item:
1688+
m2m_fields = ["indicators"]
1689+
# Remove m2m fields from item for create
1690+
item_clean = {k: v for k, v in item.items() if k not in m2m_fields}
1691+
obj = model_cls.objects.create(**item_clean)
1692+
# Set m2m fields after create
1693+
for m2m_field in m2m_fields:
1694+
values = item[m2m_field]
1695+
rel_model = obj._meta.get_field(m2m_field).related_model
1696+
rel_ids = []
1697+
for v in values:
1698+
if isinstance(v, dict):
1699+
rel_obj = rel_model.objects.create(**v)
1700+
rel_ids.append(rel_obj.id)
1701+
else:
1702+
rel_ids.append(v)
1703+
getattr(obj, m2m_field).set(rel_ids)
1704+
return obj
1705+
1706+
def m2m(data, model_cls):
1707+
ids = []
1708+
for item in data:
1709+
if isinstance(item, dict):
1710+
item_id = item.get("id")
1711+
if item_id:
1712+
ids.append(item_id)
1713+
else:
1714+
obj = create_with_m2m(item, model_cls)
1715+
ids.append(obj.id)
1716+
else:
1717+
ids.append(item)
1718+
return ids
1719+
1720+
# planned_interventions M2M handling
1721+
planned_interventions_data = self.initial_data.get("planned_interventions")
1722+
if planned_interventions_data:
1723+
final_report.planned_interventions.set(m2m(planned_interventions_data, PlannedIntervention))
1724+
1725+
# needs_identified M2M handling
1726+
needs_identified_data = self.initial_data.get("needs_identified")
1727+
if needs_identified_data:
1728+
final_report.needs_identified.set(m2m(needs_identified_data, IdentifiedNeed))
1729+
1730+
# national_society_actions M2M handling
1731+
national_society_actions_data = self.initial_data.get("national_society_actions")
1732+
if national_society_actions_data:
1733+
final_report.national_society_actions.set(m2m(national_society_actions_data, NationalSocietyAction))
1734+
1735+
# risk_security M2M handling
1736+
risk_security_data = self.initial_data.get("risk_security")
1737+
if risk_security_data:
1738+
final_report.risk_security.set(m2m(risk_security_data, RiskSecurity))
1739+
1740+
# source_information M2M handling
1741+
source_information_data = self.initial_data.get("source_information")
1742+
if source_information_data:
1743+
final_report.source_information.set(m2m(source_information_data, SourceInformation))
1744+
1745+
# proposed_action M2M handling
1746+
proposed_action_data = self.initial_data.get("proposed_action")
1747+
if proposed_action_data:
1748+
if isinstance(proposed_action_data, str):
1749+
proposed_action_data = ev(proposed_action_data)
1750+
if isinstance(proposed_action_data, dict):
1751+
proposed_action_data = [proposed_action_data]
1752+
final_report.proposed_action.set(m2m(proposed_action_data, ProposedAction))
1753+
1754+
return final_report
15241755

15251756

15261757
class CompletedDrefOperationsSerializer(serializers.ModelSerializer):

0 commit comments

Comments
 (0)