Coverage for apis_core/collections/models.py: 74%
47 statements
« prev ^ index » next coverage.py v7.6.10, created at 2025-02-19 16:54 +0000
« prev ^ index » next coverage.py v7.6.10, created at 2025-02-19 16:54 +0000
1from django.contrib.contenttypes.fields import GenericForeignKey
2from django.contrib.contenttypes.models import ContentType
3from django.db import models
5from apis_core.generic.abc import GenericModel
8class SkosCollectionManager(models.Manager):
9 def get_by_full_path(self, name: str):
10 """
11 Return a collection specified by its full path, from the root colletion
12 to the leaf collection, delimited by `|`. I.e. if there is a collection
13 named `foo` and it has a parent named `bar` and `bar` does not have a
14 parent, then you can use the string "bar|foo" to get the `foo` collection.
15 """
16 names = name.split("|")
17 parent = None
18 while names:
19 parent = self.get(parent=parent, name=names.pop(0))
20 return parent
22 def by_instance(self, instance):
23 content_type = ContentType.objects.get_for_model(instance)
24 scco = SkosCollectionContentObject.objects.filter(
25 content_type=content_type, object_id=instance.id
26 )
27 return self.get_queryset().filter(
28 pk__in=scco.values_list("collection", flat=True)
29 )
32class SkosCollection(GenericModel, models.Model):
33 """
34 SKOS collections are labeled and/or ordered groups of SKOS concepts.
35 Collections are useful where a group of concepts shares something in common,
36 and it is convenient to group them under a common label, or
37 where some concepts can be placed in a meaningful order.
39 Miles, Alistair, and Sean Bechhofer. "SKOS simple knowledge
40 organization system reference. W3C recommendation (2009)."
42 """
44 class Meta:
45 ordering = ["name"]
46 constraints = [
47 models.UniqueConstraint(
48 fields=(
49 "name",
50 "parent",
51 ),
52 name="unique_name_parent",
53 nulls_distinct=False,
54 violation_error_message="The combination of name and parent collection must be unique",
55 ),
56 models.CheckConstraint(
57 check=~models.Q(name__contains="|"),
58 name="check_name_pipe",
59 violation_error_message="The name must not contain the pipe symbol: |",
60 ),
61 ]
63 parent = models.ForeignKey("self", null=True, on_delete=models.CASCADE, blank=True)
64 name = models.CharField(
65 max_length=300,
66 verbose_name="skos:prefLabel",
67 help_text="Collection label or name",
68 )
69 label_lang = models.CharField(
70 max_length=3,
71 blank=True,
72 default="en",
73 verbose_name="skos:prefLabel language",
74 help_text="Language of preferred label given above",
75 )
76 creator = models.TextField(
77 blank=True,
78 verbose_name="dc:creator",
79 help_text="Person or organisation that created this collection"
80 "If more than one list all using a semicolon ;",
81 )
82 contributor = models.TextField(
83 blank=True,
84 verbose_name="dc:contributor",
85 help_text="Person or organisation that made contributions to the collection"
86 "If more than one list all using a semicolon ;",
87 )
88 objects = SkosCollectionManager()
90 def __str__(self):
91 return self.name
93 def children(self):
94 return SkosCollection.objects.filter(parent=self)
96 def children_tree_as_list(self):
97 childtrees = [self]
98 for child in self.children():
99 childtrees.extend(child.children_tree_as_list())
100 return childtrees
102 def add(self, instance: object):
103 content_type = ContentType.objects.get_for_model(instance)
104 SkosCollectionContentObject.objects.get_or_create(
105 collection=self, content_type=content_type, object_id=instance.id
106 )
108 def remove(self, instance: object):
109 content_type = ContentType.objects.get_for_model(instance)
110 SkosCollectionContentObject.objects.filter(
111 collection=self, content_type=content_type, object_id=instance.id
112 ).delete()
115class SkosCollectionContentObject(GenericModel, models.Model):
116 """
117 *Throughtable* datamodel to connect collections to arbitrary content
118 """
120 collection = models.ForeignKey(SkosCollection, on_delete=models.CASCADE)
122 content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
123 object_id = models.PositiveIntegerField()
124 content_object = GenericForeignKey("content_type", "object_id")
126 def __str__(self):
127 return f"{self.content_object} -> {self.collection}"