Skip to content

Commit fe4aeb5

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

File tree

1 file changed

+226
-3
lines changed

1 file changed

+226
-3
lines changed

dref/serializers.py

Lines changed: 226 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,79 @@ 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+
dref.proposed_action.set(m2m(proposed_action_data, ProposedAction))
772+
694773
if to:
695774
transaction.on_commit(lambda: send_dref_email.delay(dref.id, list(to), "Updated"))
696775
return dref
@@ -1079,7 +1158,77 @@ def update(self, instance, validated_data):
10791158
if modified_at and instance.modified_at and modified_at < instance.modified_at:
10801159
raise serializers.ValidationError({"modified_at": settings.DREF_OP_UPDATE_FINAL_REPORT_UPDATE_ERROR_MESSAGE})
10811160
validated_data["modified_at"] = timezone.now()
1082-
return super().update(instance, validated_data)
1161+
operational_update = super().update(instance, validated_data)
1162+
1163+
# Helpers for robust M2M update
1164+
1165+
def create_with_m2m(item, model_cls):
1166+
# Only handle PlannedIntervention/indicators for now
1167+
m2m_fields = []
1168+
if model_cls.__name__ == "PlannedIntervention" and "indicators" in item:
1169+
m2m_fields = ["indicators"]
1170+
# Remove m2m fields from item for create
1171+
item_clean = {k: v for k, v in item.items() if k not in m2m_fields}
1172+
obj = model_cls.objects.create(**item_clean)
1173+
# Set m2m fields after create
1174+
for m2m_field in m2m_fields:
1175+
values = item[m2m_field]
1176+
rel_model = obj._meta.get_field(m2m_field).related_model
1177+
rel_ids = []
1178+
for v in values:
1179+
if isinstance(v, dict):
1180+
rel_obj = rel_model.objects.create(**v)
1181+
rel_ids.append(rel_obj.id)
1182+
else:
1183+
rel_ids.append(v)
1184+
getattr(obj, m2m_field).set(rel_ids)
1185+
return obj
1186+
1187+
def m2m(data, model_cls):
1188+
ids = []
1189+
for item in data:
1190+
if isinstance(item, dict):
1191+
item_id = item.get("id")
1192+
if item_id:
1193+
ids.append(item_id)
1194+
else:
1195+
obj = create_with_m2m(item, model_cls)
1196+
ids.append(obj.id)
1197+
else:
1198+
ids.append(item)
1199+
return ids
1200+
1201+
# planned_interventions M2M handling
1202+
planned_interventions_data = self.initial_data.get("planned_interventions")
1203+
if planned_interventions_data:
1204+
operational_update.planned_interventions.set(m2m(planned_interventions_data, PlannedIntervention))
1205+
1206+
# needs_identified M2M handling
1207+
needs_identified_data = self.initial_data.get("needs_identified")
1208+
if needs_identified_data:
1209+
operational_update.needs_identified.set(m2m(needs_identified_data, IdentifiedNeed))
1210+
1211+
# national_society_actions M2M handling
1212+
national_society_actions_data = self.initial_data.get("national_society_actions")
1213+
if national_society_actions_data:
1214+
operational_update.national_society_actions.set(m2m(national_society_actions_data, NationalSocietyAction))
1215+
1216+
# risk_security M2M handling
1217+
risk_security_data = self.initial_data.get("risk_security")
1218+
if risk_security_data:
1219+
operational_update.risk_security.set(m2m(risk_security_data, RiskSecurity))
1220+
1221+
# source_information M2M handling
1222+
source_information_data = self.initial_data.get("source_information")
1223+
if source_information_data:
1224+
operational_update.source_information.set(m2m(source_information_data, SourceInformation))
1225+
1226+
# proposed_action M2M handling
1227+
proposed_action_data = self.initial_data.get("proposed_action")
1228+
if proposed_action_data:
1229+
operational_update.proposed_action.set(m2m(proposed_action_data, ProposedAction))
1230+
1231+
return operational_update
10831232

10841233

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

15251748

15261749
class CompletedDrefOperationsSerializer(serializers.ModelSerializer):

0 commit comments

Comments
 (0)