|
1 | 1 | import datetime |
2 | 2 | import os |
| 3 | +from ast import literal_eval as ev |
3 | 4 | from typing import List, Optional |
4 | 5 |
|
5 | 6 | from django.conf import settings |
@@ -288,7 +289,12 @@ def create(self, validated_data): |
288 | 289 | indicators = validated_data.pop("indicators", []) |
289 | 290 | intervention = super().create(validated_data) |
290 | 291 | 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) |
292 | 298 | intervention.indicators.add(ind_object) |
293 | 299 | return intervention |
294 | 300 |
|
@@ -691,6 +697,79 @@ def update(self, instance, validated_data): |
691 | 697 | raise serializers.ValidationError({"modified_at": settings.DREF_OP_UPDATE_FINAL_REPORT_UPDATE_ERROR_MESSAGE}) |
692 | 698 | validated_data["modified_at"] = timezone.now() |
693 | 699 | 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 | + |
694 | 773 | if to: |
695 | 774 | transaction.on_commit(lambda: send_dref_email.delay(dref.id, list(to), "Updated")) |
696 | 775 | return dref |
@@ -1079,7 +1158,77 @@ def update(self, instance, validated_data): |
1079 | 1158 | if modified_at and instance.modified_at and modified_at < instance.modified_at: |
1080 | 1159 | raise serializers.ValidationError({"modified_at": settings.DREF_OP_UPDATE_FINAL_REPORT_UPDATE_ERROR_MESSAGE}) |
1081 | 1160 | 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 |
1083 | 1232 |
|
1084 | 1233 |
|
1085 | 1234 | class DrefFinalReportSerializer(NestedUpdateMixin, NestedCreateMixin, ModelSerializer): |
@@ -1520,7 +1669,81 @@ def update(self, instance, validated_data): |
1520 | 1669 | raise serializers.ValidationError({"modified_at": settings.DREF_OP_UPDATE_FINAL_REPORT_UPDATE_ERROR_MESSAGE}) |
1521 | 1670 | validated_data["modified_at"] = timezone.now() |
1522 | 1671 | 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 |
1524 | 1747 |
|
1525 | 1748 |
|
1526 | 1749 | class CompletedDrefOperationsSerializer(serializers.ModelSerializer): |
|
0 commit comments