Skip to content

Commit

Permalink
fix: replace custom duplication logic with signal
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
b1rger committed Aug 30, 2023
1 parent 7fbc6e3 commit 540dca9
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 28 deletions.
28 changes: 0 additions & 28 deletions apis_core/apis_metainfo/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
3 changes: 3 additions & 0 deletions apis_core/apis_relations/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
18 changes: 18 additions & 0 deletions apis_core/apis_relations/models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import unicodedata
import copy

# from reversion import revisions as reversion
import reversion
Expand All @@ -14,6 +15,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():
Expand Down Expand Up @@ -310,6 +312,22 @@ def get_all_childs(cls_current):

super().save(*args, **kwargs)

def duplicate(self):
origin = self.__class__
signals.pre_duplicate.send(sender=origin, instance=self)

instance = copy.copy(self)

self.pk = None
self.id = None
self._state.adding = True
duplicate = self.save()

signals.post_duplicate.send(
sender=origin, instance=instance, duplicate=duplicate
)
return duplicate


class TempTriple(Triple):
review = models.BooleanField(
Expand Down
22 changes: 22 additions & 0 deletions apis_core/apis_relations/signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
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()
for rel in TempTriple.objects.filter(obj=instance):
newrel = rel.duplicate()
newrel.obj = duplicate
newrel.save()

0 comments on commit 540dca9

Please sign in to comment.