Coverage for apis_core/generic/templatetags/generic.py: 36%

99 statements  

« prev     ^ index     » next       coverage.py v7.5.3, created at 2025-06-25 10:00 +0000

1from django import template 

2from django.apps import apps 

3from django.contrib.contenttypes.models import ContentType 

4from django.core.exceptions import ObjectDoesNotExist 

5from django.shortcuts import get_object_or_404 

6 

7from apis_core.core.templatetags.core import get_model_fields 

8from apis_core.generic.abc import GenericModel 

9from apis_core.generic.helpers import template_names_via_mro 

10 

11register = template.Library() 

12 

13 

14@register.filter 

15def contenttype(model): 

16 return ContentType.objects.get_for_model(model) 

17 

18 

19@register.simple_tag 

20def modeldict(instance, fields=None, exclude=None): 

21 data = {} 

22 for f in get_model_fields(instance): 

23 if not getattr(f, "editable", False): 

24 continue 

25 if fields is not None and f.name not in fields: 

26 continue 

27 if exclude and f.name in exclude: 

28 continue 

29 field = instance._meta.get_field(f.name) 

30 try: 

31 data[field] = getattr(instance, field.name) 

32 except ObjectDoesNotExist as e: 

33 data[field] = f"{field.value_from_object(instance)} ({e})" 

34 if fn := getattr(instance, f"get_{field.name}_display", False): 

35 data[field] = fn() 

36 if getattr(field, "m2m_field_name", False): 

37 values = getattr(instance, field.name).all() 

38 data[field] = ", ".join([str(value) for value in values]) 

39 return data 

40 

41 

42@register.simple_tag 

43def contenttypes(app_labels=None): 

44 if app_labels: 

45 app_labels = app_labels.split(",") 

46 return ContentType.objects.filter(app_label__in=app_labels) 

47 return ContentType.objects.all() 

48 

49 

50def is_genericmodel(content_type: ContentType): 

51 model_class = content_type.model_class() 

52 return model_class is not None and issubclass(model_class, GenericModel) 

53 

54 

55@register.simple_tag 

56def genericmodel_content_types(): 

57 """ 

58 Retrieve all models which inherit from GenericModel class 

59 and return their ContentType. 

60 """ 

61 genericmodels = list( 

62 filter( 

63 lambda content_type: is_genericmodel(content_type), 

64 ContentType.objects.all(), 

65 ) 

66 ) 

67 return genericmodels 

68 

69 

70@register.simple_tag 

71def pure_genericmodel_content_types(): 

72 """ 

73 Retrieve all models which inherit from GenericModel class 

74 but are not Collections, Entities, Relations or History models 

75 """ 

76 parents = [] 

77 if apps.is_installed("apis_core.collections"): 

78 collections = apps.get_app_config("collections") 

79 parents.append(collections.models_module.SkosCollection) 

80 parents.append(collections.models_module.SkosCollectionContentObject) 

81 if apps.is_installed("apis_core.relations"): 

82 relations = apps.get_app_config("relations") 

83 parents.append(relations.models_module.Relation) 

84 if apps.is_installed("apis_core.history"): 

85 history = apps.get_app_config("history") 

86 parents.append(history.models_module.APISHistoryTableBase) 

87 if apps.is_installed("apis_core.apis_entities"): 

88 entities = apps.get_app_config("apis_entities") 

89 parents.append(entities.models_module.AbstractEntity) 

90 genericmodels = [ 

91 ct 

92 for ct in set(genericmodel_content_types()) 

93 if not issubclass(ct.model_class(), tuple(parents)) 

94 ] 

95 return genericmodels 

96 

97 

98@register.filter 

99def get_attribute(obj, attribute): 

100 return getattr(obj, attribute, None) 

101 

102 

103@register.filter 

104def content_type_count(content_type): 

105 """ 

106 Return the number of objects having a specific content type 

107 """ 

108 return content_type.model_class().objects.count() 

109 

110 

111@register.simple_tag 

112def template_list(obj, suffix): 

113 return template_names_via_mro(type(obj), suffix) 

114 

115 

116@register.simple_tag(takes_context=True) 

117def any_view_permission(context, content_types): 

118 user = context.request.user 

119 return any( 

120 [user.has_perm(ct.model_class().get_view_permission()) for ct in content_types] 

121 ) 

122 

123 

124@register.simple_tag 

125def content_types_by_natural_keys(natural_keys: tuple() = ()) -> [ContentType]: 

126 """ 

127 Convert a list of natural keys to a list of ContentType models 

128 If any of the natural keys does not refer to an existing model, raise a 404 

129 """ 

130 content_types = [] 

131 for key in natural_keys: 

132 app_label, model = key.split(".") 

133 content_type = get_object_or_404(ContentType, app_label=app_label, model=model) 

134 content_types.append(content_type) 

135 return content_types 

136 

137 

138@register.simple_tag 

139def natural_keys_by_content_types(content_types: tuple() = ()) -> [str]: 

140 """ 

141 Convert a list of ContentType models to their natural key 

142 """ 

143 natural_keys = [] 

144 for content_type in content_types: 

145 natural_keys.append(content_type.app_label + "." + content_type.model) 

146 return natural_keys 

147 

148 

149@register.filter 

150def split(string: str = "", delimiter=",") -> [str]: 

151 """ 

152 Split a string by a specific delimiter and also strip the string of 

153 leading and trailing whitespaces. 

154 """ 

155 return map(str.strip, string.split(delimiter)) 

156 

157 

158@register.simple_tag 

159def model_field_template_lookup_list(model, field, suffix="") -> [str]: 

160 """ 

161 generate a template path based on the modelname, the fieldname and 

162 the suffix. return a list with this template path and a fallback 

163 path. 

164 """ 

165 content_type = ContentType.objects.get_for_model(model) 

166 path = f"{content_type.app_label}/partials/{content_type.model}_{field.name}_{suffix}.html" 

167 return [path, f"generic/partials/default_model_field_{suffix}.html"]