Coverage for apis_core / generic / templatetags / generic.py: 77%
102 statements
« prev ^ index » next coverage.py v7.14.0, created at 2026-06-15 07:31 +0000
« prev ^ index » next coverage.py v7.14.0, created at 2026-06-15 07:31 +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
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
11register = template.Library()
14@register.filter
15def contenttype(model):
16 return ContentType.objects.get_for_model(model)
19@register.simple_tag
20def modeldict(instance, fields=None, exclude=None, exclude_noneditable=True):
21 data = {}
22 for f in get_model_fields(instance):
23 if not getattr(f, "editable", False) and exclude_noneditable:
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
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()
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)
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
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 if apps.is_installed("apis_core.entities"):
91 entities = apps.get_app_config("entities")
92 parents.append(entities.module.abc.Entity)
93 genericmodels = [
94 ct
95 for ct in set(genericmodel_content_types())
96 if not issubclass(ct.model_class(), tuple(parents))
97 ]
98 return genericmodels
101@register.filter
102def get_attribute(obj, attribute):
103 return getattr(obj, attribute, None)
106@register.filter
107def content_type_count(content_type):
108 """
109 Return the number of objects having a specific content type
110 """
111 return content_type.model_class().objects.count()
114@register.simple_tag
115def template_list(obj, folder="", prefix="", suffix=""):
116 return template_names_via_mro(
117 type(obj), folder=folder, prefix=prefix, suffix=suffix
118 )
121@register.simple_tag(takes_context=True)
122def any_view_permission(context, content_types):
123 user = context.request.user
124 return any(
125 [user.has_perm(ct.model_class().get_view_permission()) for ct in content_types]
126 )
129@register.simple_tag
130def content_types_by_natural_keys(natural_keys: tuple = ()) -> list[ContentType]:
131 """
132 Convert a list of natural keys to a list of ContentType models
133 If any of the natural keys does not refer to an existing model, raise a 404
134 """
135 content_types = []
136 for key in natural_keys:
137 app_label, model = key.split(".")
138 content_type = get_object_or_404(ContentType, app_label=app_label, model=model)
139 content_types.append(content_type)
140 return content_types
143@register.simple_tag
144def natural_keys_by_content_types(content_types: tuple = ()) -> list[str]:
145 """
146 Convert a list of ContentType models to their natural key
147 """
148 natural_keys = []
149 for content_type in content_types:
150 natural_keys.append(content_type.app_label + "." + content_type.model)
151 return natural_keys
154@register.filter
155def split(string: str = "", delimiter=",") -> list[str]:
156 """
157 Split a string by a specific delimiter and also strip the string of
158 leading and trailing whitespaces.
159 """
160 return list(map(str.strip, string.split(delimiter)))
163@register.simple_tag
164def model_field_template_lookup_list(model, field, suffix="") -> list[str]:
165 """
166 generate a template path based on the modelname, the fieldname and
167 the suffix. return a list with this template path and a fallback
168 path.
169 """
170 content_type = ContentType.objects.get_for_model(model)
171 path = f"{content_type.app_label}/partials/{content_type.model}_{field.name}_{suffix}.html"
172 return [path, f"generic/partials/default_model_field_{suffix}.html"]