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

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 _ 

5 

6from apis_core.generic.abc import GenericModel 

7 

8 

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 ) 

18 

19 

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. 

26 

27 Miles, Alistair, and Sean Bechhofer. "SKOS simple knowledge 

28 organization system reference. W3C recommendation (2009)." 

29 

30 """ 

31 

32 class Meta: 

33 ordering = ["name"] 

34 verbose_name = _("collection") 

35 verbose_name_plural = _("collections") 

36 

37 class Config(GenericModel.Config): 

38 overview_section = _("Collections") 

39 

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() 

65 

66 def __str__(self): 

67 return self.name 

68 

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 ) 

74 

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() 

80 

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 ) 

92 

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 

98 

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() 

104 

105 

106class SkosCollectionContentObject(GenericModel, models.Model): 

107 """ 

108 *Throughtable* datamodel to connect collections to arbitrary content 

109 """ 

110 

111 class Meta: 

112 verbose_name = _("collection content object") 

113 verbose_name_plural = _("collection content objects") 

114 

115 class Config(GenericModel.Config): 

116 overview_section = _("Collections") 

117 

118 collection = models.ForeignKey(SkosCollection, on_delete=models.CASCADE) 

119 

120 content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) 

121 object_id = models.PositiveIntegerField() 

122 content_object = GenericForeignKey("content_type", "object_id") 

123 

124 def __str__(self): 

125 if self.content_object: 

126 return f"{self.content_object} -> {self.collection}" 

127 return super().__str__()