Skip to content

Commit

Permalink
refactor(generic,apis_metainfo): move duplicate logic to GenericModel
Browse files Browse the repository at this point in the history
  • Loading branch information
b1rger committed Dec 17, 2024
1 parent 3e20f06 commit 4697065
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 47 deletions.
46 changes: 0 additions & 46 deletions apis_core/apis_metainfo/models.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
import logging
from urllib.parse import urlsplit

from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ImproperlyConfigured, ValidationError
from django.db import models
from django.db.models.fields.related import ForeignKey, ManyToManyField
from django.db.models.fields.related_descriptors import ForwardManyToOneDescriptor
from django.forms import model_to_dict
from model_utils.managers import InheritanceManager

from apis_core.generic import signals
from apis_core.generic.abc import GenericModel
from apis_core.utils import rdf
from apis_core.utils import settings as apis_settings
from apis_core.utils.normalize import clean_uri

logger = logging.getLogger(__name__)


class RootObject(GenericModel, models.Model):
"""
Expand All @@ -41,46 +35,6 @@ def save(self, *args, **kwargs):
self.self_contenttype = ContentType.objects.get_for_model(self)
super().save(*args, **kwargs)

def duplicate(self):
origin = self.__class__
signals.pre_duplicate.send(sender=origin, instance=self)
# usually, copying instances would work like
# https://docs.djangoproject.com/en/4.2/topics/db/queries/#copying-model-instances
# but we are working with abstract classes,
# so we have to do it by hand using model_to_dict:(
objdict = model_to_dict(self)

# remove unique fields from dict representation
unique_fields = [field for field in self._meta.fields if field.unique]
for field in unique_fields:
logger.info(f"Duplicating {self}: ignoring unique field {field.name}")
objdict.pop(field.name, None)

# remove related fields from dict representation
related_fields = [
field for field in self._meta.get_fields() if field.is_relation
]
for field in related_fields:
objdict.pop(field.name, None)

newobj = type(self).objects.create(**objdict)

for field in related_fields:
# we are not using `isinstance` because we want to
# differentiate between different levels of inheritance
if type(field) is ForeignKey:
setattr(newobj, field.name, getattr(self, field.name))
if type(field) is ManyToManyField:
objfield = getattr(newobj, field.name)
values = getattr(self, field.name).all()
objfield.set(values)

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

duplicate.alters_data = True


class InheritanceForwardManyToOneDescriptor(ForwardManyToOneDescriptor):
def get_queryset(self, **hints):
Expand Down
53 changes: 52 additions & 1 deletion apis_core/generic/abc.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
import logging

from django.contrib.contenttypes.models import ContentType
from django.db.models import BooleanField, CharField, TextField
from django.db.models.fields.related import ForeignKey, ManyToManyField
from django.db.models.query import QuerySet
from django.forms import model_to_dict
from django.urls import reverse

from apis_core.generic.helpers import permission_fullname
from apis_core.generic.signals import post_merge_with, pre_merge_with
from apis_core.generic.signals import (
post_duplicate,
post_merge_with,
pre_duplicate,
pre_merge_with,
)

logger = logging.getLogger(__name__)


class GenericModel:
Expand Down Expand Up @@ -163,3 +174,43 @@ def merge_with(self, entities):

for ent in entities:
ent.delete()

def duplicate(self):
origin = self.__class__
pre_duplicate.send(sender=origin, instance=self)
# usually, copying instances would work like
# https://docs.djangoproject.com/en/4.2/topics/db/queries/#copying-model-instances
# but we are working with abstract classes,
# so we have to do it by hand using model_to_dict:(
objdict = model_to_dict(self)

# remove unique fields from dict representation
unique_fields = [field for field in self._meta.fields if field.unique]
for field in unique_fields:
logger.info(f"Duplicating {self}: ignoring unique field {field.name}")
objdict.pop(field.name, None)

# remove related fields from dict representation
related_fields = [
field for field in self._meta.get_fields() if field.is_relation
]
for field in related_fields:
objdict.pop(field.name, None)

newobj = type(self).objects.create(**objdict)

for field in related_fields:
# we are not using `isinstance` because we want to
# differentiate between different levels of inheritance
if type(field) is ForeignKey:
setattr(newobj, field.name, getattr(self, field.name))
if type(field) is ManyToManyField:
objfield = getattr(newobj, field.name)
values = getattr(self, field.name).all()
objfield.set(values)

newobj.save()
post_duplicate.send(sender=origin, instance=self, duplicate=newobj)
return newobj

duplicate.alters_data = True

0 comments on commit 4697065

Please sign in to comment.