From f21c641cfa2f2d118bc5b983a4b6fb4d4ba250ba Mon Sep 17 00:00:00 2001 From: Kelvin Muchiri Date: Tue, 22 Oct 2024 11:09:32 +0300 Subject: [PATCH 1/5] refactor to use SoftDeleteManager --- onadata/apps/logger/models/attachment.py | 5 +++++ onadata/apps/logger/models/data_view.py | 8 ++++++-- onadata/apps/logger/models/entity.py | 6 ++++-- onadata/apps/logger/models/entity_list.py | 9 ++++---- onadata/apps/logger/models/instance.py | 12 ++++++++--- onadata/apps/logger/models/project.py | 5 +++-- onadata/apps/logger/models/xform.py | 4 +++- onadata/libs/models/base_model.py | 25 +++++++++++++++++++++++ 8 files changed, 60 insertions(+), 14 deletions(-) diff --git a/onadata/apps/logger/models/attachment.py b/onadata/apps/logger/models/attachment.py index 96f55b6061..09085ac8de 100644 --- a/onadata/apps/logger/models/attachment.py +++ b/onadata/apps/logger/models/attachment.py @@ -2,6 +2,7 @@ """ Attachment model. """ + import hashlib import mimetypes import os @@ -9,6 +10,8 @@ from django.contrib.auth import get_user_model from django.db import models +from onadata.libs.models.base_model import SoftDeleteManager + def get_original_filename(filename): """Returns the filename removing the hashed random string added to it when we have @@ -83,6 +86,8 @@ class Attachment(models.Model): on_delete=models.SET_NULL, ) + objects = SoftDeleteManager() + class Meta: app_label = "logger" diff --git a/onadata/apps/logger/models/data_view.py b/onadata/apps/logger/models/data_view.py index 51278f4f25..6339449ee1 100644 --- a/onadata/apps/logger/models/data_view.py +++ b/onadata/apps/logger/models/data_view.py @@ -2,6 +2,7 @@ """ DataView model class """ + import datetime import json @@ -9,11 +10,12 @@ from django.contrib.gis.db import models from django.db import connection from django.db.models.signals import post_delete, post_save +from django.db.utils import DataError from django.utils import timezone from django.utils.translation import gettext as _ -from django.db.utils import DataError from onadata.apps.viewer.parsed_instance_tools import get_where_clause +from onadata.libs.models.base_model import SoftDeleteManager from onadata.libs.models.sorting import ( # noqa pylint: disable=unused-import json_order_by, json_order_by_params, @@ -22,8 +24,8 @@ from onadata.libs.utils.cache_tools import ( # noqa pylint: disable=unused-import DATAVIEW_COUNT, DATAVIEW_LAST_SUBMISSION_TIME, - XFORM_LINKED_DATAVIEWS, PROJ_OWNER_CACHE, + XFORM_LINKED_DATAVIEWS, safe_delete, ) from onadata.libs.utils.common_tags import ( @@ -123,6 +125,8 @@ class DataView(models.Model): blank=True, ) + objects = SoftDeleteManager() + class Meta: app_label = "logger" verbose_name = _("Data View") diff --git a/onadata/apps/logger/models/entity.py b/onadata/apps/logger/models/entity.py index 14bfb039cd..516400b0fe 100644 --- a/onadata/apps/logger/models/entity.py +++ b/onadata/apps/logger/models/entity.py @@ -2,8 +2,8 @@ Entity model """ -import uuid import importlib +import uuid from django.contrib.auth import get_user_model from django.db import models, transaction @@ -12,7 +12,7 @@ from onadata.apps.logger.models.entity_list import EntityList from onadata.apps.logger.models.instance import Instance from onadata.apps.logger.models.registration_form import RegistrationForm -from onadata.libs.models import BaseModel +from onadata.libs.models import BaseModel, SoftDeleteManager User = get_user_model() @@ -30,6 +30,8 @@ class Entity(BaseModel): deleted_at = models.DateTimeField(null=True, blank=True) deleted_by = models.ForeignKey(User, null=True, on_delete=models.SET_NULL) + objects = SoftDeleteManager() + def __str__(self) -> str: return f"{self.pk}|{self.entity_list}" diff --git a/onadata/apps/logger/models/entity_list.py b/onadata/apps/logger/models/entity_list.py index 669614a4d9..cb094f25dc 100644 --- a/onadata/apps/logger/models/entity_list.py +++ b/onadata/apps/logger/models/entity_list.py @@ -6,17 +6,16 @@ from django.contrib.auth.models import Group, Permission from django.contrib.contenttypes.fields import GenericRelation from django.db import models, transaction -from django.utils.translation import gettext_lazy as _ from django.utils import timezone +from django.utils.translation import gettext_lazy as _ - -from guardian.models import UserObjectPermissionBase, GroupObjectPermissionBase from guardian.compat import user_model_label +from guardian.models import GroupObjectPermissionBase, UserObjectPermissionBase from onadata.apps.logger.models.project import Project from onadata.apps.logger.models.xform import clear_project_cache from onadata.apps.main.models.meta_data import MetaData -from onadata.libs.models import BaseModel +from onadata.libs.models import BaseModel, SoftDeleteManager from onadata.libs.utils.model_tools import queryset_iterator User = get_user_model() @@ -43,6 +42,8 @@ class EntityList(BaseModel): deleted_at = models.DateTimeField(null=True, blank=True) deleted_by = models.ForeignKey(User, null=True, on_delete=models.SET_NULL) + objects = SoftDeleteManager() + def __str__(self): return f"{self.name}|{self.project}" diff --git a/onadata/apps/logger/models/instance.py b/onadata/apps/logger/models/instance.py index b487cd123d..ecbda526eb 100644 --- a/onadata/apps/logger/models/instance.py +++ b/onadata/apps/logger/models/instance.py @@ -2,6 +2,7 @@ """ Instance model class """ + import math import sys from datetime import datetime @@ -18,15 +19,19 @@ from django.urls import reverse from django.utils import timezone from django.utils.translation import gettext as _ -from multidb.pinning import use_master from celery import current_task from deprecated import deprecated +from multidb.pinning import use_master from taggit.managers import TaggableManager from onadata.apps.logger.models.submission_review import SubmissionReview from onadata.apps.logger.models.survey_type import SurveyType -from onadata.apps.logger.models.xform import XFORM_TITLE_LENGTH, XForm +from onadata.apps.logger.models.xform import ( + XFORM_TITLE_LENGTH, + SoftDeleteManager, + XForm, +) from onadata.apps.logger.xform_instance_parser import ( XFormInstanceParser, clean_and_parse_xml, @@ -35,11 +40,11 @@ from onadata.celeryapp import app from onadata.libs.data.query import get_numeric_fields from onadata.libs.utils.cache_tools import ( - PROJECT_DATE_MODIFIED_CACHE, DATAVIEW_COUNT, IS_ORG, PROJ_NUM_DATASET_CACHE, PROJ_SUB_DATE_CACHE, + PROJECT_DATE_MODIFIED_CACHE, XFORM_COUNT, XFORM_DATA_VERSIONS, XFORM_SUBMISSION_COUNT_FOR_DAY, @@ -693,6 +698,7 @@ class Instance(models.Model, InstanceBaseClass): has_a_review = models.BooleanField(_("has_a_review"), default=False) tags = TaggableManager() + objects = SoftDeleteManager() class Meta: app_label = "logger" diff --git a/onadata/apps/logger/models/project.py b/onadata/apps/logger/models/project.py index fdef798b0e..91dfb4f44c 100644 --- a/onadata/apps/logger/models/project.py +++ b/onadata/apps/logger/models/project.py @@ -2,6 +2,7 @@ """ Project model class """ + from django.apps import apps from django.conf import settings from django.contrib.auth import get_user_model @@ -15,7 +16,7 @@ from guardian.shortcuts import assign_perm, get_perms_for_model from taggit.managers import TaggableManager -from onadata.libs.models.base_model import BaseModel +from onadata.libs.models.base_model import BaseModel, SoftDeleteManager from onadata.libs.utils.common_tags import OWNER_TEAM_NAME # pylint: disable=invalid-name @@ -118,7 +119,7 @@ class Project(BaseModel): on_delete=models.SET_NULL, ) - objects = models.Manager() + objects = SoftDeleteManager() tags = TaggableManager(related_name="project_tags") prefetched = PrefetchManager() diff --git a/onadata/apps/logger/models/xform.py b/onadata/apps/logger/models/xform.py index af4add620a..918877c495 100644 --- a/onadata/apps/logger/models/xform.py +++ b/onadata/apps/logger/models/xform.py @@ -2,6 +2,7 @@ """ The XForm model """ + # pylint: disable=too-many-lines import hashlib import json @@ -33,7 +34,7 @@ from taggit.managers import TaggableManager from onadata.apps.logger.xform_instance_parser import XLSFormError, clean_and_parse_xml -from onadata.libs.models.base_model import BaseModel +from onadata.libs.models.base_model import BaseModel, SoftDeleteManager from onadata.libs.utils.cache_tools import ( PROJ_BASE_FORMS_CACHE, PROJ_FORMS_CACHE, @@ -894,6 +895,7 @@ class XForm(XFormMixin, BaseModel): is_merged_dataset = models.BooleanField(default=False) is_instance_json_regenerated = models.BooleanField(default=False) tags = TaggableManager() + objects = SoftDeleteManager() class Meta: app_label = "logger" diff --git a/onadata/libs/models/base_model.py b/onadata/libs/models/base_model.py index 074aa9fa8d..b8918e8ce5 100644 --- a/onadata/libs/models/base_model.py +++ b/onadata/libs/models/base_model.py @@ -2,6 +2,7 @@ """ BaseModel abstract class - sets date_created/date_modified fields. """ + from django.db import models @@ -15,3 +16,27 @@ class BaseModel(models.Model): class Meta: abstract = True + + +class SoftDeleteQuerySet(models.QuerySet): + """Custom queryset that only returns objects that have not been deleted""" + + def active(self): + """Return only objects that have not been deleted""" + return self.filter(deleted_at__isnull=True) + + def all_with_deleted(self): + """Return all objects, including those that have been deleted""" + return self + + +class SoftDeleteManager(models.Manager): + """Custom manager that only returns objects that have not been deleted""" + + def get_queryset(self): + """Return queryset that only returns objects that have not been deleted""" + return SoftDeleteQuerySet(self.model, using=self._db).active() + + def all_with_deleted(self): + """Return all objects, including those that have been deleted""" + return self.get_queryset().all_with_deleted() From 6f3804e9d220821e58842dbfdeb9f210011749f6 Mon Sep 17 00:00:00 2001 From: Kelvin Muchiri Date: Tue, 22 Oct 2024 11:16:11 +0300 Subject: [PATCH 2/5] fix ImportError fix ImportError: cannot import name 'SoftDeleteManager' from 'onadata.libs.models' --- onadata/apps/logger/models/attachment.py | 2 +- onadata/apps/logger/models/data_view.py | 2 +- onadata/libs/models/__init__.py | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/onadata/apps/logger/models/attachment.py b/onadata/apps/logger/models/attachment.py index 09085ac8de..290b9ccb6e 100644 --- a/onadata/apps/logger/models/attachment.py +++ b/onadata/apps/logger/models/attachment.py @@ -10,7 +10,7 @@ from django.contrib.auth import get_user_model from django.db import models -from onadata.libs.models.base_model import SoftDeleteManager +from onadata.libs.models import SoftDeleteManager def get_original_filename(filename): diff --git a/onadata/apps/logger/models/data_view.py b/onadata/apps/logger/models/data_view.py index 6339449ee1..6de02b0d58 100644 --- a/onadata/apps/logger/models/data_view.py +++ b/onadata/apps/logger/models/data_view.py @@ -15,7 +15,7 @@ from django.utils.translation import gettext as _ from onadata.apps.viewer.parsed_instance_tools import get_where_clause -from onadata.libs.models.base_model import SoftDeleteManager +from onadata.libs.models import SoftDeleteManager from onadata.libs.models.sorting import ( # noqa pylint: disable=unused-import json_order_by, json_order_by_params, diff --git a/onadata/libs/models/__init__.py b/onadata/libs/models/__init__.py index 1de471a73a..d6163cf9e1 100644 --- a/onadata/libs/models/__init__.py +++ b/onadata/libs/models/__init__.py @@ -2,4 +2,6 @@ """ Model utility classes and functions. """ + from .base_model import BaseModel # noqa +from .base_model import SoftDeleteManager # noqa From a01ed71c88fc56e95f98a85412c496fd0848d149 Mon Sep 17 00:00:00 2001 From: Kelvin Muchiri Date: Tue, 22 Oct 2024 11:24:03 +0300 Subject: [PATCH 3/5] suppress lint warning too-many-lines --- onadata/apps/logger/models/instance.py | 1 + 1 file changed, 1 insertion(+) diff --git a/onadata/apps/logger/models/instance.py b/onadata/apps/logger/models/instance.py index ecbda526eb..62c2162027 100644 --- a/onadata/apps/logger/models/instance.py +++ b/onadata/apps/logger/models/instance.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# pylint: disable=too-many-lines """ Instance model class """ From 889abb7bbc74109fe273ee9bd865d46afa87c987 Mon Sep 17 00:00:00 2001 From: Kelvin Muchiri Date: Tue, 22 Oct 2024 12:31:22 +0300 Subject: [PATCH 4/5] fix failing tests --- onadata/apps/api/tests/models/test_project.py | 85 ++++++++++--------- .../tests/viewsets/test_attachment_viewset.py | 5 +- .../tests/viewsets/test_briefcase_viewset.py | 6 +- .../tests/viewsets/test_metadata_viewset.py | 5 +- .../api/tests/viewsets/test_xform_viewset.py | 6 +- onadata/apps/main/models/meta_data.py | 8 +- onadata/libs/models/base_model.py | 2 +- 7 files changed, 62 insertions(+), 55 deletions(-) diff --git a/onadata/apps/api/tests/models/test_project.py b/onadata/apps/api/tests/models/test_project.py index 283476e89d..690ae913a0 100644 --- a/onadata/apps/api/tests/models/test_project.py +++ b/onadata/apps/api/tests/models/test_project.py @@ -1,19 +1,15 @@ import os from onadata.apps.api import tools -from onadata.apps.api.tests.models.test_abstract_models import\ - TestAbstractModels - -from onadata.apps.logger.models.xform import XForm +from onadata.apps.api.tests.models.test_abstract_models import TestAbstractModels from onadata.apps.logger.models.instance import Instance from onadata.apps.logger.models.project import Project +from onadata.apps.logger.models.xform import XForm from onadata.apps.logger.xform_instance_parser import XLSFormError - from onadata.apps.main.tests.test_base import TestBase class TestProject(TestAbstractModels, TestBase): - def test_create_organization_project(self): organization = self._create_organization("modilabs", self.user) project_name = "demo" @@ -21,9 +17,10 @@ def test_create_organization_project(self): self.assertIsInstance(project, Project) self.assertEqual(project.name, project_name) - user_deno = self._create_user('deno', 'deno') + user_deno = self._create_user("deno", "deno") project = tools.create_organization_project( - organization, project_name, user_deno) + organization, project_name, user_deno + ) self.assertIsNone(project) def test_project_soft_delete_works_when_no_exception_is_raised(self): @@ -36,19 +33,17 @@ def test_project_soft_delete_works_when_no_exception_is_raised(self): organization = self._create_organization("modilabs", self.user) project_name = "demo" project = self._create_project(organization, project_name, self.user) - sample_xml = """Phonefuctionalbroken""" # noqa + sample_xml = """Phonefuctionalbroken""" # noqa XForm.objects.create( user=self.user, xml=sample_xml, - id_string='domestic_animals', - title='domestic_animals_in_kenyan_homes', - project=project + id_string="domestic_animals", + title="domestic_animals_in_kenyan_homes", + project=project, ) project.soft_delete() - self.assertEqual( - 1, Project.objects.filter(deleted_at__isnull=False).count()) - self.assertEqual( - 1, XForm.objects.filter(deleted_at__isnull=False).count()) + self.assertEqual(0, Project.objects.count()) + self.assertEqual(0, XForm.objects.count()) def test_project_detetion_reverts_when_an_exception_raised(self): """ @@ -63,26 +58,33 @@ def test_project_detetion_reverts_when_an_exception_raised(self): organization = self._create_organization("modilabs", self.user) project_name = "demo" project = self._create_project(organization, project_name, self.user) - sample_json = '{"default_language": "default", ' \ - '"id_string": "Water_2011_03_17", "children": [], ' \ - '"name": "Water_2011_03_17", ' \ - '"title": "Water_2011_03_17", "type": "survey"}' - f = open(os.path.join( - os.path.dirname(os.path.abspath(__file__)), - '../../../', - 'logger/tests/', - "Water_Translated_2011_03_10.xml" - )) + sample_json = ( + '{"default_language": "default", ' + '"id_string": "Water_2011_03_17", "children": [], ' + '"name": "Water_2011_03_17", ' + '"title": "Water_2011_03_17", "type": "survey"}' + ) + f = open( + os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "../../../", + "logger/tests/", + "Water_Translated_2011_03_10.xml", + ) + ) xml = f.read() f.close() xform = XForm.objects.create( - xml=xml, user=self.user, json=sample_json, project=project) + xml=xml, user=self.user, json=sample_json, project=project + ) - f = open(os.path.join(os.path.dirname( - os.path.abspath(__file__)), - '../../../', - 'logger/tests/', - 'Water_Translated_2011_03_10_2011-03-10_14-38-28.xml') + f = open( + os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "../../../", + "logger/tests/", + "Water_Translated_2011_03_10_2011-03-10_14-38-28.xml", + ) ) xml = f.read() f.close() @@ -92,24 +94,27 @@ def test_project_detetion_reverts_when_an_exception_raised(self): try: XForm.objects.raw( "UPDATE logger_xform SET id_string='a New ID String' \ - WHERE id={};".format(xform.id))[0] + WHERE id={};".format( + xform.id + ) + )[0] except TypeError: pass xform_refetch = XForm.objects.all()[0] - self.assertEqual('a New ID String', xform_refetch.id_string) + self.assertEqual("a New ID String", xform_refetch.id_string) with self.assertRaises(XLSFormError): project.soft_delete() - self.assertEqual(1, Project.objects.filter( - deleted_at__isnull=True).count()) + self.assertEqual(1, Project.objects.filter(deleted_at__isnull=True).count()) self.assertIsNone(project.deleted_at) - self.assertEqual(1, XForm.objects.filter( - project=project, deleted_at__isnull=True).count()) + self.assertEqual( + 1, + XForm.objects.filter(project=project, deleted_at__isnull=True).count(), + ) # Try deleting the Xform; it should also roll back due to the exception with self.assertRaises(XLSFormError): XForm.objects.all()[0].soft_delete() - self.assertEqual(1, XForm.objects.filter( - deleted_at__isnull=True).count()) + self.assertEqual(1, XForm.objects.filter(deleted_at__isnull=True).count()) self.assertIsNone(XForm.objects.all()[0].deleted_at) diff --git a/onadata/apps/api/tests/viewsets/test_attachment_viewset.py b/onadata/apps/api/tests/viewsets/test_attachment_viewset.py index 95b0948d1e..e5ec3b0eb4 100644 --- a/onadata/apps/api/tests/viewsets/test_attachment_viewset.py +++ b/onadata/apps/api/tests/viewsets/test_attachment_viewset.py @@ -2,6 +2,7 @@ """ Test Attachment viewsets. """ + import os from django.utils import timezone @@ -211,14 +212,12 @@ def test_data_list_with_xform_in_delete_async(self): self.assertNotEqual(response.get("Cache-Control"), None) self.assertEqual(response.status_code, 200) self.assertTrue(isinstance(response.data, list)) - initial_count = len(response.data) self.xform.deleted_at = timezone.now() self.xform.save() request = self.factory.get("/", data={"xform": self.xform.pk}, **self.extra) response = self.list_view(request) - self.assertEqual(response.status_code, 200) - self.assertEqual(len(response.data), initial_count - 1) + self.assertEqual(response.status_code, 404) def test_list_view_filter_by_xform(self): self._submit_transport_instance_w_attachment() diff --git a/onadata/apps/api/tests/viewsets/test_briefcase_viewset.py b/onadata/apps/api/tests/viewsets/test_briefcase_viewset.py index 1b91199eba..b254e76ba8 100644 --- a/onadata/apps/api/tests/viewsets/test_briefcase_viewset.py +++ b/onadata/apps/api/tests/viewsets/test_briefcase_viewset.py @@ -2,6 +2,7 @@ """ Test BriefcaseViewset """ + import codecs import os import shutil @@ -792,9 +793,10 @@ def test_query_optimization_fence(self): self.assertEqual(instances.count(), optimized_instances.count()) op_sql_query = ( 'SELECT "logger_instance"."id", "logger_instance"."uuid" FROM "logger_instance"' - f' WHERE "logger_instance"."id" IN ({optimized_instances[0].get("pk")},' + f' WHERE ("logger_instance"."deleted_at" IS NULL AND "logger_instance"."id"' + f' IN ({optimized_instances[0].get("pk")},' f' {optimized_instances[1].get("pk")}, {optimized_instances[2].get("pk")},' - f' {optimized_instances[3].get("pk")})' + f' {optimized_instances[3].get("pk")}))' ) self.assertEqual(str(optimized_instances.query), op_sql_query) diff --git a/onadata/apps/api/tests/viewsets/test_metadata_viewset.py b/onadata/apps/api/tests/viewsets/test_metadata_viewset.py index 2640f6fd5c..8b6ca8a261 100644 --- a/onadata/apps/api/tests/viewsets/test_metadata_viewset.py +++ b/onadata/apps/api/tests/viewsets/test_metadata_viewset.py @@ -2,6 +2,7 @@ """ Tests the MetaDataViewSet. """ + # pylint: disable=too-many-lines import os from builtins import open @@ -242,9 +243,7 @@ def test_delete_xform_deletes_media_metadata(self): self.xform.soft_delete() # Confirm that all metadata was deleted response2 = self.view(request) - self.assertEqual(response2.status_code, 200) - self.assertEqual(len(response2.data), 0) - self.assertEqual(response2.data, []) + self.assertEqual(response2.status_code, 404) def test_windows_csv_file_upload_to_metadata(self): data_value = "transportation.csv" diff --git a/onadata/apps/api/tests/viewsets/test_xform_viewset.py b/onadata/apps/api/tests/viewsets/test_xform_viewset.py index ab4e6e3be6..5245f15814 100644 --- a/onadata/apps/api/tests/viewsets/test_xform_viewset.py +++ b/onadata/apps/api/tests/viewsets/test_xform_viewset.py @@ -2,6 +2,7 @@ """ Tests the XForm viewset. """ + from __future__ import unicode_literals import codecs @@ -3629,7 +3630,6 @@ def test_delete_xform_async(self, mock_get_status): with HTTMock(enketo_mock): mock_get_status.return_value = {"job_status": "PENDING"} self._publish_xls_form_to_project() - count = XForm.objects.count() view = XFormViewSet.as_view( { "delete": "delete_async", @@ -3642,7 +3642,7 @@ def test_delete_xform_async(self, mock_get_status): self.assertEqual(response.status_code, 202) self.assertTrue("job_uuid" in response.data) self.assertTrue("time_async_triggered" in response.data) - self.assertEqual(count, XForm.objects.count()) + self.assertEqual(0, XForm.objects.count()) view = XFormViewSet.as_view({"get": "delete_async"}) @@ -3654,7 +3654,7 @@ def test_delete_xform_async(self, mock_get_status): self.assertEqual(response.status_code, 202) self.assertEqual(response.data, {"job_status": "PENDING"}) - xform = XForm.objects.get(pk=formid) + xform = XForm.objects.all_with_deleted().get(pk=formid) self.assertIsNotNone(xform.deleted_at) self.assertTrue("deleted-at" in xform.id_string) diff --git a/onadata/apps/main/models/meta_data.py b/onadata/apps/main/models/meta_data.py index ed9ab47875..a445e9ce24 100644 --- a/onadata/apps/main/models/meta_data.py +++ b/onadata/apps/main/models/meta_data.py @@ -2,13 +2,14 @@ """ MetaData model """ + from __future__ import unicode_literals +import hashlib import logging import mimetypes import os from contextlib import closing -import hashlib from django.conf import settings from django.contrib.contenttypes.fields import GenericForeignKey @@ -23,9 +24,10 @@ import requests +from onadata.libs.models import SoftDeleteManager from onadata.libs.utils.cache_tools import ( - XFORM_METADATA_CACHE, XFORM_MANIFEST_CACHE, + XFORM_METADATA_CACHE, safe_delete, ) from onadata.libs.utils.common_tags import ( @@ -203,7 +205,7 @@ class MetaData(models.Model): object_id = models.PositiveIntegerField(null=True, blank=True) content_object = GenericForeignKey("content_type", "object_id") - objects = models.Manager() + objects = SoftDeleteManager() class Meta: app_label = "main" diff --git a/onadata/libs/models/base_model.py b/onadata/libs/models/base_model.py index b8918e8ce5..37f20d7df0 100644 --- a/onadata/libs/models/base_model.py +++ b/onadata/libs/models/base_model.py @@ -39,4 +39,4 @@ def get_queryset(self): def all_with_deleted(self): """Return all objects, including those that have been deleted""" - return self.get_queryset().all_with_deleted() + return SoftDeleteQuerySet(self.model, using=self._db) From b4a3f3438dd2778d4b980393930848366bec015a Mon Sep 17 00:00:00 2001 From: Kelvin Muchiri Date: Tue, 22 Oct 2024 13:05:18 +0300 Subject: [PATCH 5/5] fix failing tests --- onadata/apps/logger/models/instance.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/onadata/apps/logger/models/instance.py b/onadata/apps/logger/models/instance.py index 62c2162027..ef095837ce 100644 --- a/onadata/apps/logger/models/instance.py +++ b/onadata/apps/logger/models/instance.py @@ -392,7 +392,8 @@ def update_project_date_modified(instance_id, _): # the etag value of the projects endpoint try: instance = ( - Instance.objects.select_related("xform__project") + Instance.objects.all_with_deleted() + .select_related("xform__project") .only("xform__project__date_modified") .get(pk=instance_id) )