From f3c5818de06e34eb7465c4d7b1ba2eb766189ca3 Mon Sep 17 00:00:00 2001 From: Birger Schacht Date: Fri, 18 Aug 2023 12:42:39 +0200 Subject: [PATCH] fix: replace custom duplication logic with signal For duplicating entities, there was custom logic to copy ManyToManyField - which was tailored to TempTriples. This had the downside of simply creating new objects by hand. This commit introduces a `duplicate` method in the Triple model, which implements the functionality of duplicating itself and also triggers pre_- and post_duplicate signals. This has the advantage that instances that point to the Triple (like References) can also be duplicated using signals. --- apis_core/apis_metainfo/models.py | 28 ---------------------------- apis_core/apis_relations/apps.py | 3 +++ apis_core/apis_relations/models.py | 19 +++++++++++++++++++ apis_core/apis_relations/signals.py | 18 ++++++++++++++++++ 4 files changed, 40 insertions(+), 28 deletions(-) create mode 100644 apis_core/apis_relations/signals.py diff --git a/apis_core/apis_metainfo/models.py b/apis_core/apis_metainfo/models.py index 336f7ac5a..9e233b87b 100644 --- a/apis_core/apis_metainfo/models.py +++ b/apis_core/apis_metainfo/models.py @@ -95,34 +95,6 @@ def duplicate(self): for field in related_fields: # we are not using `isinstance` because we want to # differentiate between different levels of inheritance - if type(field) is ManyToOneRel: - # Get a list of fields of the related model that are unique - # because `related_field`s do not have a unique attribute - # we are using getattr - # Either use the related_name or contruct ith from the fields name - relname = field.related_name or f"{field.name}_set" - # Iterate through the objects in the queryset, get the subclass of the - # entry, remove the unique attributes and create a copy of the object. - # Then save the copied object - # This is limited to models with the objects_inheritance attribute, so - # basically it is tailored to TempTriples - if getattr(field.related_model, "objects_inheritance", False): - for entry in getattr(self, relname).all(): - relmodel = field.related_model - # create a copy based on the subclass - entry_copy = relmodel.objects_inheritance.get_subclass( - id=entry.id - ) - model_fields = relmodel._meta.get_fields() - unique_fields = [ - f.name for f in model_fields if getattr(f, "unique", False) - ] - for key in unique_fields: - setattr(entry_copy, key, None) - entry_copy.pk = None - entry_copy._state.adding = True - setattr(entry_copy, field.field.name, newobj) - entry_copy.save() if type(field) is ForeignKey: setattr(newobj, field.name, getattr(self, field.name)) if type(field) is ManyToManyField: diff --git a/apis_core/apis_relations/apps.py b/apis_core/apis_relations/apps.py index a4fb2f702..9c916bc02 100644 --- a/apis_core/apis_relations/apps.py +++ b/apis_core/apis_relations/apps.py @@ -4,3 +4,6 @@ class RelationsConfig(AppConfig): default_auto_field = "django.db.models.AutoField" name = "apis_core.apis_relations" + + def ready(self): + from . import signals diff --git a/apis_core/apis_relations/models.py b/apis_core/apis_relations/models.py index 94c994be6..a71ad21ff 100644 --- a/apis_core/apis_relations/models.py +++ b/apis_core/apis_relations/models.py @@ -14,6 +14,7 @@ # from apis_core.apis_entities.models import Person from apis_core.apis_metainfo.models import RootObject from apis_core.utils import DateParser +from apis_core.apis_metainfo import signals def find_if_user_accepted(): @@ -310,6 +311,24 @@ def get_all_childs(cls_current): super().save(*args, **kwargs) + def duplicate(self): + origin = self.__class__ + signals.pre_duplicate.send(sender=origin, instance=self) + + oldid = self.pk + + self.pk = None + self.id = None + self._state.adding = True + duplicate = self.save() + + instance = origin.objects.get(pk=oldid) + + signals.post_duplicate.send( + sender=origin, instance=instance, duplicate=duplicate + ) + return duplicate + class TempTriple(Triple): review = models.BooleanField( diff --git a/apis_core/apis_relations/signals.py b/apis_core/apis_relations/signals.py new file mode 100644 index 000000000..2fbab4966 --- /dev/null +++ b/apis_core/apis_relations/signals.py @@ -0,0 +1,18 @@ +from apis_core.apis_relations.models import TempTriple +from apis_core.apis_metainfo.models import RootObject +from apis_core.apis_metainfo.signals import post_duplicate +from django.dispatch import receiver + +import logging + +logger = logging.getLogger(__name__) + + +@receiver(post_duplicate) +def copy_relations(sender, instance, duplicate, **kwargs): + logger.info(f"Copying relations from {instance} to {duplicate}") + if isinstance(instance, RootObject): + for rel in TempTriple.objects.filter(subj=instance): + newrel = rel.duplicate() + newrel.subj = duplicate + newrel.save()