|
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,83 @@ 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 | + 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 | + |
694 | 777 | if to: |
695 | 778 | transaction.on_commit(lambda: send_dref_email.delay(dref.id, list(to), "Updated")) |
696 | 779 | return dref |
@@ -1079,7 +1162,81 @@ def update(self, instance, validated_data): |
1079 | 1162 | if modified_at and instance.modified_at and modified_at < instance.modified_at: |
1080 | 1163 | raise serializers.ValidationError({"modified_at": settings.DREF_OP_UPDATE_FINAL_REPORT_UPDATE_ERROR_MESSAGE}) |
1081 | 1164 | 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 |
1083 | 1240 |
|
1084 | 1241 |
|
1085 | 1242 | class DrefFinalReportSerializer(NestedUpdateMixin, NestedCreateMixin, ModelSerializer): |
@@ -1520,7 +1677,81 @@ def update(self, instance, validated_data): |
1520 | 1677 | raise serializers.ValidationError({"modified_at": settings.DREF_OP_UPDATE_FINAL_REPORT_UPDATE_ERROR_MESSAGE}) |
1521 | 1678 | validated_data["modified_at"] = timezone.now() |
1522 | 1679 | 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 |
1524 | 1755 |
|
1525 | 1756 |
|
1526 | 1757 | class CompletedDrefOperationsSerializer(serializers.ModelSerializer): |
|
0 commit comments