Coverage for apis_core/collections/models.py: 65%
54 statements
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-28 06:34 +0000
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-28 06:34 +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"]
35 class Config(GenericModel.Config):
36 overview_section = _("Collections")
38 name = models.CharField(
39 max_length=300,
40 verbose_name="skos:prefLabel",
41 help_text="Collection label or name",
42 )
43 label_lang = models.CharField(
44 max_length=3,
45 blank=True,
46 default="en",
47 verbose_name="skos:prefLabel language",
48 help_text="Language of preferred label given above",
49 )
50 creator = models.TextField(
51 blank=True,
52 verbose_name="dc:creator",
53 help_text="Person or organisation that created this collection"
54 "If more than one list all using a semicolon ;",
55 )
56 contributor = models.TextField(
57 blank=True,
58 verbose_name="dc:contributor",
59 help_text="Person or organisation that made contributions to the collection"
60 "If more than one list all using a semicolon ;",
61 )
62 objects = SkosCollectionManager()
64 def __str__(self):
65 return self.name
67 def add(self, instance: object):
68 content_type = ContentType.objects.get_for_model(instance)
69 SkosCollectionContentObject.objects.get_or_create(
70 collection=self, content_type=content_type, object_id=instance.pk
71 )
73 def remove(self, instance: object):
74 content_type = ContentType.objects.get_for_model(instance)
75 SkosCollectionContentObject.objects.filter(
76 collection=self, content_type=content_type, object_id=instance.pk
77 ).delete()
79 @property
80 def parent_collection(self):
81 content_type = ContentType.objects.get_for_model(self)
82 sccos = SkosCollectionContentObject.objects.filter(
83 content_type=content_type, object_id=self.id
84 )
85 if len(sccos) == 1:
86 return sccos.first().collection
87 raise SkosCollection.MultipleObjectsReturned(
88 f'"{self}" is part of multiple collections'
89 )
91 def list(self, content_type=None):
92 qs = SkosCollectionContentObject.objects.filter(collection=self)
93 if content_type is not None:
94 return qs.filter(content_type=content_type)
95 return qs
97 def contains(self, instance: object):
98 content_type = ContentType.objects.get_for_model(instance)
99 return SkosCollectionContentObject.objects.filter(
100 collection=self, content_type=content_type, object_id=instance.pk
101 ).exists()
104class SkosCollectionContentObject(GenericModel, models.Model):
105 """
106 *Throughtable* datamodel to connect collections to arbitrary content
107 """
109 class Config(GenericModel.Config):
110 overview_section = _("Collections")
112 collection = models.ForeignKey(SkosCollection, on_delete=models.CASCADE)
114 content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
115 object_id = models.PositiveIntegerField()
116 content_object = GenericForeignKey("content_type", "object_id")
118 def __str__(self):
119 if self.content_object:
120 return f"{self.content_object} -> {self.collection}"
121 return super().__str__()