Coverage for apis_core/collections/models.py: 68%
59 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-10-28 13:07 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-10-28 13:07 +0000
1from django.contrib.contenttypes.fields import GenericForeignKey
2from django.contrib.contenttypes.models import ContentType
3from django.db import models
4from django.utils.translation import gettext_lazy as _
6from apis_core.generic.abc import GenericModel
9class SkosCollectionManager(models.Manager):
10 def by_instance(self, instance):
11 content_type = ContentType.objects.get_for_model(instance)
12 scco = SkosCollectionContentObject.objects.filter(
13 content_type=content_type, object_id=instance.id
14 )
15 return self.get_queryset().filter(
16 pk__in=scco.values_list("collection", flat=True)
17 )
20class SkosCollection(GenericModel, models.Model):
21 """
22 SKOS collections are labeled and/or ordered groups of SKOS concepts.
23 Collections are useful where a group of concepts shares something in common,
24 and it is convenient to group them under a common label, or
25 where some concepts can be placed in a meaningful order.
27 Miles, Alistair, and Sean Bechhofer. "SKOS simple knowledge
28 organization system reference. W3C recommendation (2009)."
30 """
32 class Meta:
33 ordering = ["name"]
34 verbose_name = _("collection")
35 verbose_name_plural = _("collections")
37 class Config(GenericModel.Config):
38 overview_section = _("Collections")
40 name = models.CharField(
41 max_length=300,
42 verbose_name="skos:prefLabel",
43 help_text="Collection label or name",
44 )
45 label_lang = models.CharField(
46 max_length=3,
47 blank=True,
48 default="en",
49 verbose_name="skos:prefLabel language",
50 help_text="Language of preferred label given above",
51 )
52 creator = models.TextField(
53 blank=True,
54 verbose_name="dc:creator",
55 help_text="Person or organisation that created this collection"
56 "If more than one list all using a semicolon ;",
57 )
58 contributor = models.TextField(
59 blank=True,
60 verbose_name="dc:contributor",
61 help_text="Person or organisation that made contributions to the collection"
62 "If more than one list all using a semicolon ;",
63 )
64 objects = SkosCollectionManager()
66 def __str__(self):
67 return self.name
69 def add(self, instance: object):
70 content_type = ContentType.objects.get_for_model(instance)
71 SkosCollectionContentObject.objects.get_or_create(
72 collection=self, content_type=content_type, object_id=instance.pk
73 )
75 def remove(self, instance: object):
76 content_type = ContentType.objects.get_for_model(instance)
77 SkosCollectionContentObject.objects.filter(
78 collection=self, content_type=content_type, object_id=instance.pk
79 ).delete()
81 @property
82 def parent_collection(self):
83 content_type = ContentType.objects.get_for_model(self)
84 sccos = SkosCollectionContentObject.objects.filter(
85 content_type=content_type, object_id=self.id
86 )
87 if len(sccos) == 1:
88 return sccos.first().collection
89 raise SkosCollection.MultipleObjectsReturned(
90 f'"{self}" is part of multiple collections'
91 )
93 def list(self, content_type=None):
94 qs = SkosCollectionContentObject.objects.filter(collection=self)
95 if content_type is not None:
96 return qs.filter(content_type=content_type)
97 return qs
99 def contains(self, instance: object):
100 content_type = ContentType.objects.get_for_model(instance)
101 return SkosCollectionContentObject.objects.filter(
102 collection=self, content_type=content_type, object_id=instance.pk
103 ).exists()
106class SkosCollectionContentObject(GenericModel, models.Model):
107 """
108 *Throughtable* datamodel to connect collections to arbitrary content
109 """
111 class Meta:
112 verbose_name = _("collection content object")
113 verbose_name_plural = _("collection content objects")
115 class Config(GenericModel.Config):
116 overview_section = _("Collections")
118 collection = models.ForeignKey(SkosCollection, on_delete=models.CASCADE)
120 content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
121 object_id = models.PositiveIntegerField()
122 content_object = GenericForeignKey("content_type", "object_id")
124 def __str__(self):
125 if self.content_object:
126 return f"{self.content_object} -> {self.collection}"
127 return super().__str__()