diff options
| -rw-r--r-- | archaeological_context_records/models.py | 15 | ||||
| -rw-r--r-- | archaeological_context_records/tests.py | 30 | ||||
| -rw-r--r-- | archaeological_finds/models_finds.py | 22 | ||||
| -rw-r--r-- | archaeological_operations/models.py | 16 | ||||
| -rw-r--r-- | archaeological_warehouse/models.py | 4 | ||||
| -rw-r--r-- | ishtar_common/models.py | 4 | ||||
| -rw-r--r-- | ishtar_common/models_common.py | 41 | ||||
| -rw-r--r-- | ishtar_common/utils.py | 7 | ||||
| -rw-r--r-- | ishtar_common/wizards.py | 1 |
9 files changed, 123 insertions, 17 deletions
diff --git a/archaeological_context_records/models.py b/archaeological_context_records/models.py index 4da7e35e2..87300b899 100644 --- a/archaeological_context_records/models.py +++ b/archaeological_context_records/models.py @@ -27,7 +27,7 @@ from django.contrib.postgres.indexes import GinIndex from django.contrib.sites.models import Site from django.db import transaction, OperationalError, IntegrityError from django.db.models import Q -from django.db.models.signals import post_delete, post_save, m2m_changed +from django.db.models.signals import m2m_changed, pre_delete, post_delete, post_save from django.urls import reverse, reverse_lazy from ishtar_common.utils import get_generated_id, gettext_lazy as _, pgettext_lazy, \ @@ -59,6 +59,7 @@ from ishtar_common.models import ( RelationItem, Town, get_current_profile, + geo_item_pre_delete, document_attached_changed, HistoryModel, GeoItem, @@ -1485,10 +1486,11 @@ class ContextRecord( Return sub object list that will be deleted :return: {"Sub object type": ["Sub object 1", "Sub object 2", ...]} """ + data = super().get_deleted_data() if not self.base_finds.count(): - return {} + return data lbl = str(_("Base finds")) - data = {lbl: []} + data[lbl] = [] for item in self.base_finds.all(): data[lbl].append(str(item)) for key, value in item.get_deleted_data().items(): @@ -1514,11 +1516,11 @@ class ContextRecord( def context_record_post_save(sender, **kwargs): - cached_label_changed(sender=sender, **kwargs) - post_save_geo(sender=sender, **kwargs) instance = kwargs.get("instance", None) - if not instance or not instance.pk: + if not instance or not instance.pk or getattr(instance, "__delete", False): return + cached_label_changed(sender=sender, **kwargs) + post_save_geo(sender=sender, **kwargs) profile = get_current_profile() if profile.parent_relations_engine == "T": ContextRecordTree._update_self_relation(instance.pk) # on creation: manage self relation @@ -1532,6 +1534,7 @@ def context_record_post_save(sender, **kwargs): cached_label_changed(Find, instance=f) +pre_delete.connect(geo_item_pre_delete, sender=ContextRecord) post_save.connect(context_record_post_save, sender=ContextRecord) m2m_changed.connect(document_attached_changed, sender=ContextRecord.documents.through) m2m_changed.connect(geodata_attached_changed, sender=ContextRecord.geodata.through) diff --git a/archaeological_context_records/tests.py b/archaeological_context_records/tests.py index 942ad83f1..0338a2f20 100644 --- a/archaeological_context_records/tests.py +++ b/archaeological_context_records/tests.py @@ -24,6 +24,7 @@ import locale from django.apps import apps from django.conf import settings from django.contrib.auth.models import Permission, Group +from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError from django.core.files.uploadedfile import SimpleUploadedFile from django.template.defaultfilters import slugify @@ -600,6 +601,35 @@ class ContextRecordTest(ContextRecordInit, TestCase): # same unit and other operation -> 1 self.assertEqual(cr3.custom_index, 1) + def test_cascade_delete(self): + cr1 = self.context_records[0] + # add a geo_vector associated to the town + source_content_type_pk = ContentType.objects.get( + app_label="ishtar_common", + model="town" + ).pk + geo_vector = models.GeoVectorData.objects.create( + source_content_type_id=source_content_type_pk, + source_id=cr1.town_id, + name="geo", + comment="This is a comment." + ) + cr1.geodata.add(geo_vector) + BaseFind = apps.get_model("archaeological_finds", "BaseFind") + bf = BaseFind.objects.create(context_record=cr1) + bf.geodata.add(geo_vector) + Find = apps.get_model("archaeological_finds", "Find") + find = Find.objects.create() + bf.find.add(find) + dating = models.ContextRecordDating.objects.create(context_record=cr1) + cr1.operation.delete() + self.assertEqual(models.ContextRecord.objects.filter(id=cr1.id).count(), 0) + self.assertEqual(Find.objects.filter(id=find.id).count(), 0) + self.assertEqual(BaseFind.objects.filter(id=bf.id).count(), 0) + self.assertEqual(models.ContextRecordDating.objects.filter(id=dating.id).count(), + 0) + self.assertEqual(models.GeoVectorData.objects.filter(id=geo_vector.id).count(), 1) + class ContextRecordQATest(ContextRecordInit, TestCase): fixtures = CONTEXT_RECORD_TOWNS_FIXTURES diff --git a/archaeological_finds/models_finds.py b/archaeological_finds/models_finds.py index 5eed32772..56232165f 100644 --- a/archaeological_finds/models_finds.py +++ b/archaeological_finds/models_finds.py @@ -71,6 +71,8 @@ from ishtar_common.models import ( QuickAction, SearchVectorConfig, ValueGetter, + main_item_pre_delete, + geo_item_pre_delete ) from ishtar_common.models_common import HistoricalRecords, SerializeItem, \ GeoVectorData, geodata_attached_changed @@ -938,21 +940,23 @@ class BaseFind( Return sub object list that will be deleted :return: {"Sub object type": ["Sub object 1", "Sub object 2", ...]} """ + data = super().get_deleted_data() if self.find.count() != 1: - return {} + return data lbl = str(_("Finds")) - data = {lbl: []} + data[lbl] = [] for item in self.find.all(): data[lbl].append(str(item)) return data def post_save_basefind(sender, **kwargs): + instance = kwargs.get("instance", None) + if not instance or not instance.pk or getattr(instance, "__delete", False): + return cached_label_changed(sender, **kwargs) post_save_geo(sender, **kwargs) instance = kwargs.get("instance", None) - if not instance or not instance.pk: - return for f in instance.find.all(): cached_label_changed(Find, instance=f) @@ -961,10 +965,12 @@ def pre_delete_basefind(sender, **kwargs): instance = kwargs["instance"] if not instance or not instance.pk: return + geo_item_pre_delete(sender, **kwargs) q = Find.objects.filter(base_finds__pk=instance.pk) for find in q.all(): if find.base_finds.count() == 1: # only associated to the deleted base find find.__base_find_deleted = True # prevent loop + find.__delete = True find.delete() @@ -3828,6 +3834,14 @@ def pre_clean_find(sender, **kwargs): instance.upstream_treatment.delete() +def pre_delete_find(sender, **kwargs): + instance = kwargs["instance"] + if not instance or not instance.pk: + return + main_item_pre_delete(sender, **kwargs) + + +pre_delete.connect(pre_delete_find, sender=Find) post_save.connect(cached_label_changed, sender=Find) pre_delete.connect(pre_clean_find, sender=Find) diff --git a/archaeological_operations/models.py b/archaeological_operations/models.py index 6ef6ced88..452874abe 100644 --- a/archaeological_operations/models.py +++ b/archaeological_operations/models.py @@ -32,7 +32,7 @@ from django.contrib.postgres.indexes import GinIndex from django.contrib.sites.models import Site from django.db import transaction, OperationalError, IntegrityError from django.db.models import Q, Max, Sum -from django.db.models.signals import post_save, m2m_changed, post_delete +from django.db.models.signals import m2m_changed, post_save, post_delete, pre_delete from django.forms import ValidationError from django.urls import reverse, reverse_lazy from ishtar_common.data_importer import post_importer_action @@ -73,6 +73,7 @@ from ishtar_common.models import ( QuickAction, MainItem, HierarchicalType, + geo_item_pre_delete, ) from ishtar_common.models_common import Department, GeoVectorData, HistoricalRecords,\ geodata_attached_changed, geotown_attached_changed @@ -1015,6 +1016,11 @@ class ArchaeologicalSite( def site_post_save(sender, **kwargs): + instance = kwargs.get("instance", None) + if not instance or not instance.pk: + return + if getattr(instance, "__delete", False): + return cached_label_changed(sender=sender, **kwargs) post_save_geo(sender=sender, **kwargs) @@ -1937,10 +1943,10 @@ class Operation( Return sub object list that will be deleted :return: {"Sub object type": ["Sub object 1", "Sub object 2", ...]} """ - data = {} + data = super().get_deleted_data() if self.administrative_act.count(): lbl = str(_("Administrative act")) - data = {lbl: []} + data[lbl] = [] for item in self.administrative_act.all(): data[lbl].append(str(item)) if self.context_record.count(): @@ -2696,7 +2702,8 @@ for attr in Operation.HISTORICAL_M2M: def operation_post_save(sender, **kwargs): - if not kwargs["instance"]: + instance = kwargs.get("instance", None) + if not instance or not instance.pk or getattr(instance, "__delete", False): return post_save_geo(sender=sender, **kwargs) @@ -2743,6 +2750,7 @@ def operation_post_save(sender, **kwargs): post_save.connect(operation_post_save, sender=Operation) +pre_delete.connect(geo_item_pre_delete, sender=Operation) def operation_town_m2m_changed(sender, **kwargs): diff --git a/archaeological_warehouse/models.py b/archaeological_warehouse/models.py index dc9aa340a..4a5294632 100644 --- a/archaeological_warehouse/models.py +++ b/archaeological_warehouse/models.py @@ -58,6 +58,7 @@ from ishtar_common.models_common import ( QuickAction, MainItem, Merge, + main_item_pre_delete ) from ishtar_common.model_merging import merge_model_objects from ishtar_common.utils import ( @@ -2170,6 +2171,9 @@ def container_post_save(sender, **kwargs): def container_pre_delete(sender, **kwargs): instance = kwargs["instance"] + if not instance or not instance.pk: + return + main_item_pre_delete(sender, **kwargs) if getattr(instance, "_no_pre_delete", False): return q = Container.objects.filter(container_tree_child__container_parent=instance) diff --git a/ishtar_common/models.py b/ishtar_common/models.py index aa9224be7..86be555ca 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -187,6 +187,8 @@ from ishtar_common.models_common import ( State, StatisticItem, Town, + main_item_pre_delete, + geo_item_pre_delete ) __all__ = [ @@ -247,6 +249,8 @@ __all__ = [ "GeoOriginType", "GeoProviderType", "GeoBufferType", + "geo_item_pre_delete", + "main_item_pre_delete" ] logger = logging.getLogger(__name__) diff --git a/ishtar_common/models_common.py b/ishtar_common/models_common.py index 00ff8babd..986ba209d 100644 --- a/ishtar_common/models_common.py +++ b/ishtar_common/models_common.py @@ -40,7 +40,7 @@ from django.urls import reverse, NoReverseMatch from django.core.validators import validate_slug from django.db import connection, transaction, OperationalError, IntegrityError from django.db.models import JSONField, Q, Count, Max -from django.db.models.signals import post_save, post_delete, m2m_changed +from django.db.models.signals import m2m_changed, post_save, post_delete, pre_delete from django.template import loader from django.template.defaultfilters import slugify from django.utils import timezone @@ -3110,6 +3110,8 @@ def geodata_attached_changed(sender, **kwargs): if not profile.mapping: return instance = kwargs.get("instance", None) + if getattr(instance, "__delete", False): + return model = kwargs.get("model", None) pk_set = kwargs.get("pk_set", None) @@ -3244,6 +3246,43 @@ class GeographicItem(models.Model): self._geodata_list = lst return lst + @property + def q_associated_source_geovectordata(self): + content_type = ContentType.objects.get_for_model(self.__class__) + return GeoVectorData.objects.filter(source_content_type=content_type, + source_id=self.pk) + + def get_deleted_data(self) -> dict: + """ + Return sub object list that will be deleted + :return: {"Sub object type": ["Sub object 1", "Sub object 2", ...]} + """ + if not self.q_associated_source_geovectordata.count(): + return {} + lbl = str(_("Geographic - Vector data")) + data = {lbl: []} + for geodata in self.q_associated_source_geovectordata.all(): + data[lbl].append(f"{self.__class__._meta.verbose_name} - {geodata}") + return data + + +def main_item_pre_delete(sender, **kwargs): + instance = kwargs.get("instance", None) + if not instance or not instance.pk: + return + instance.__delete = True + + +def geo_item_pre_delete(sender, **kwargs): + instance = kwargs.get("instance", None) + if not instance or not instance.pk: + return + instance.__delete = True + instance._post_save_geo_ok = True + instance._post_saved_geo = True + instance.geodata.clear() + instance.q_associated_source_geovectordata.delete() + class PermissionQuery(models.Model): model = models.ForeignKey(ContentType, related_name="permissions", diff --git a/ishtar_common/utils.py b/ishtar_common/utils.py index bd79814e9..bdd7a67e5 100644 --- a/ishtar_common/utils.py +++ b/ishtar_common/utils.py @@ -2402,20 +2402,23 @@ def manage_m2m(obj, kwargs): obj.skip_history_when_saving = True elif not obj.history_modifier: obj.skip_history_when_saving = True + obj._post_save_geo_ok = True obj.save() def related_historization_changed(sender, **kwargs): rel_obj = kwargs.get("instance", None) - if not rel_obj or not getattr(rel_obj, "CURRENT_MODEL_ATTR", None): + if not rel_obj or not getattr(rel_obj, "CURRENT_MODEL_ATTR", None) or getattr( + rel_obj, "__delete", False): return obj = getattr(rel_obj, rel_obj.CURRENT_MODEL_ATTR) + obj._post_save_geo_ok = True manage_m2m(obj, kwargs) def m2m_historization_changed(sender, **kwargs): obj = kwargs.get("instance", None) - if not obj: + if not obj or getattr(obj, "__delete", False): return manage_m2m(obj, kwargs) diff --git a/ishtar_common/wizards.py b/ishtar_common/wizards.py index d29ebf365..a7313b13b 100644 --- a/ishtar_common/wizards.py +++ b/ishtar_common/wizards.py @@ -1941,6 +1941,7 @@ class MultipleDeletionWizard(MultipleItemWizard): for obj in objs: lbls.append(str(obj)) try: + obj.__delete = True obj.delete() except ObjectDoesNotExist: pass |
