Coverage for apis_core/relations/signals.py: 53%
55 statements
« prev ^ index » next coverage.py v7.5.3, created at 2025-10-10 13:36 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2025-10-10 13:36 +0000
1import logging
3from django.contrib.contenttypes.models import ContentType
4from django.db.models.signals import post_delete, post_save
5from django.dispatch import receiver
7from apis_core.generic.signals import post_duplicate, post_merge_with
8from apis_core.relations.models import Relation
10logger = logging.getLogger(__name__)
13@receiver(post_duplicate)
14def copy_relations(sender, instance, duplicate, **kwargs):
15 logger.info(f"Copying relations from {instance!r} to {duplicate!r}")
16 content_type = ContentType.objects.get_for_model(instance)
17 subj_rels = Relation.objects.filter(
18 subj_content_type=content_type, subj_object_id=instance.id
19 ).select_subclasses()
20 obj_rels = Relation.objects.filter(
21 obj_content_type=content_type, obj_object_id=instance.id
22 ).select_subclasses()
23 for rel in subj_rels:
24 rel.pk = None
25 rel.id = None
26 rel.subj_object_id = duplicate.id
27 rel.save()
28 for rel in obj_rels:
29 rel.pk = None
30 rel.id = None
31 rel.obj_object_id = duplicate.id
32 rel.save()
35@receiver(post_merge_with)
36def merge_relations(sender, instance, entities, **kwargs):
37 for ent in entities:
38 logger.info(f"Merging relations from {ent!r} into {instance!r}")
39 content_type = ContentType.objects.get_for_model(ent)
40 Relation.objects.filter(
41 subj_content_type=content_type, subj_object_id=ent.id
42 ).update(subj_object_id=instance.id)
43 Relation.objects.filter(
44 obj_content_type=content_type, obj_object_id=ent.id
45 ).update(obj_object_id=instance.id)
48@receiver(post_delete)
49def set_relations_null(sender, instance, using, origin, **kwargs):
50 content_type = ContentType.objects.get_for_model(instance)
51 object_id = instance.pk
52 if isinstance(object_id, int):
53 Relation.objects.filter(
54 subj_content_type=content_type, subj_object_id=object_id
55 ).update(subj_object_id=None)
56 Relation.objects.filter(
57 obj_content_type=content_type, obj_object_id=object_id
58 ).update(obj_object_id=None)
61@receiver(post_save)
62def create_relations(sender, instance, created, raw, using, update_fields, **kwargs):
63 """
64 This signal looks at the `create_relations_to_uris` attribute of a model
65 instance. The attribute should contain a dict mapping between a relation
66 name and mapping between they key `obj` or `subj` and a list of URIs.
67 The signal then tries to create relations between the instance and the
68 subjects or objects listed in the relation dict mapping.
69 An example for the dict mapping would be:
70 "myapp.livesin" = {
71 curies = ["https://example.org/123"],
72 obj = "apis_ontology.place"
73 }
74 """
76 # disable the handler during fixture loading
77 if raw:
78 return
79 relations = getattr(instance, "create_relations_to_uris", {})
80 for relation, details in relations.items():
81 relation_model = ContentType.objects.get_by_natural_key(
82 *relation.split(".")
83 ).model_class()
85 target = details.get("obj", None) or details.get("subj", None)
86 target_content_type = ContentType.objects.get_by_natural_key(*target.split("."))
87 related_model = target_content_type.model_class()
89 for related_uri in details["curies"]:
90 try:
91 related_instance = related_model.import_from(uri=related_uri)
92 if details.get("obj"):
93 relation_model.object.create_between_instances(
94 instance, related_instance
95 )
96 else:
97 relation_model.object.create_between_instances(
98 related_instance, instance
99 )
100 except Exception as e:
101 logger.error(
102 "Could not create relation to %s due to %s", related_uri, e
103 )