Coverage for apis_core/relations/templatetags/relations.py: 47%

55 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-09-30 06:11 +0000

1from django import template 

2from django.apps import apps 

3from django.contrib.contenttypes.models import ContentType 

4from django.db.models import Case, Q, Value, When 

5 

6from apis_core.generic.helpers import first_member_match, module_paths 

7from apis_core.relations.models import Relation 

8from apis_core.relations.tables import RelationsListTable 

9from apis_core.relations.utils import relation_content_types 

10 

11register = template.Library() 

12 

13 

14@register.simple_tag 

15def possible_relation_types_from(obj) -> list[ContentType]: 

16 return relation_content_types(any_model=type(obj)) 

17 

18 

19@register.simple_tag 

20def get_relation_targets_from(obj) -> list[ContentType]: 

21 relations = relation_content_types(any_model=type(obj)) 

22 types = set() 

23 for model in [relation.model_class() for relation in relations]: 

24 if isinstance(obj, model.obj_model_type()): 

25 types.add(model.subj_model_type()) 

26 if isinstance(obj, model.subj_model_type()): 

27 types.add(model.obj_model_type()) 

28 return sorted( 

29 list(map(ContentType.objects.get_for_model, types)), key=lambda x: x.name 

30 ) 

31 

32 

33@register.simple_tag 

34def relations_from(from_obj, relation_type: ContentType = None): 

35 from_content_type = ContentType.objects.get_for_model(from_obj) 

36 relation = Relation 

37 if relation_type is not None: 

38 relation = relation_type.model_class() 

39 

40 relations = ( 

41 relation.objects.filter( 

42 Q(subj_content_type=from_content_type, subj_object_id=from_obj.id) 

43 | Q(obj_content_type=from_content_type, obj_object_id=from_obj.id) 

44 ) 

45 .annotate( 

46 forward=Case( 

47 When( 

48 subj_content_type=from_content_type, 

49 subj_object_id=from_obj.id, 

50 then=Value(True), 

51 ), 

52 default=Value(False), 

53 ) 

54 ) 

55 .select_subclasses() 

56 ) 

57 return relations 

58 

59 

60@register.simple_tag(takes_context=True) 

61def relations_list_table(context, relations, suffix=None): 

62 suffixes = ["RelationsTable"] 

63 if suffix: 

64 suffixes.insert(0, f"{suffix}RelationsTable") 

65 table_modules = () 

66 for suffix in suffixes: 

67 table_modules += module_paths( 

68 type(context["object"]), path="tables", suffix=suffix 

69 ) 

70 table_class = first_member_match(table_modules, RelationsListTable) 

71 return table_class(relations, request=context["request"]) 

72 

73 

74@register.simple_tag 

75def get_relation_content_types(): 

76 return relation_content_types() 

77 

78 

79@register.simple_tag 

80def get_relation_between_content_types(source: ContentType, target: ContentType): 

81 return relation_content_types( 

82 combination=(source.model_class(), target.model_class()) 

83 ) 

84 

85 

86@register.simple_tag(takes_context=True) 

87def relations_instances_from_relation_types(context, relation_types): 

88 relations = [] 

89 for relation_type in relation_types: 

90 relations.extend(relations_from(context["object"], relation_type)) 

91 return relations 

92 

93 

94@register.simple_tag 

95def get_relationmodels() -> list[Relation]: 

96 """ 

97 Return a list of `Relation` models 

98 """ 

99 return list( 

100 filter(lambda x: issubclass(x, Relation) and x != Relation, apps.get_models()) 

101 )