Coverage for apis_core/relations/utils.py: 26%

35 statements  

« prev     ^ index     » next       coverage.py v7.6.8, created at 2024-12-20 09:24 +0000

1import functools 

2 

3from django.contrib.contenttypes.models import ContentType 

4 

5from apis_core.relations.models import Relation 

6 

7 

8def is_relation(ct: ContentType) -> bool: 

9 mc = ct.model_class() 

10 return ( 

11 issubclass(mc, Relation) 

12 and hasattr(mc, "subj_model") 

13 and hasattr(mc, "obj_model") 

14 ) 

15 

16 

17@functools.cache 

18def relation_content_types( 

19 subj_model=None, obj_model=None, any_model=None, combination=(None, None) 

20) -> set[ContentType]: 

21 allcts = list( 

22 filter( 

23 lambda contenttype: contenttype.model_class() is not None, 

24 ContentType.objects.all(), 

25 ) 

26 ) 

27 relationcts = list(filter(lambda contenttype: is_relation(contenttype), allcts)) 

28 if subj_model is not None: 

29 relationcts = list( 

30 filter( 

31 lambda contenttype: subj_model in contenttype.model_class().subj_list(), 

32 relationcts, 

33 ) 

34 ) 

35 if obj_model is not None: 

36 relationcts = list( 

37 filter( 

38 lambda contenttype: obj_model in contenttype.model_class().obj_list(), 

39 relationcts, 

40 ) 

41 ) 

42 if any_model is not None: 

43 relationcts = list( 

44 filter( 

45 lambda contenttype: any_model in contenttype.model_class().obj_list() 

46 or any_model in contenttype.model_class().subj_list(), 

47 relationcts, 

48 ) 

49 ) 

50 if all(combination): 

51 left, right = combination 

52 rels = list( 

53 filter( 

54 lambda contenttype: right in contenttype.model_class().obj_list() 

55 and left in contenttype.model_class().subj_list(), 

56 relationcts, 

57 ) 

58 ) 

59 rels.extend( 

60 list( 

61 filter( 

62 lambda contenttype: left in contenttype.model_class().obj_list() 

63 and right in contenttype.model_class().subj_list(), 

64 relationcts, 

65 ) 

66 ) 

67 ) 

68 relationcts = rels 

69 return set(relationcts) 

70 

71 

72def relation_match_target(relation, target: ContentType) -> bool: 

73 """ 

74 test if a relation points to a target 

75 this function should not be cached, because the `forward` attribute 

76 is an annotation that does not seem to be part of the relation, so 

77 if cached, method could be called with another `forward` value and 

78 return the wrong result 

79 """ 

80 if relation.forward and relation.obj_content_type == target: 

81 return True 

82 if not relation.forward and relation.subj_content_type == target: 

83 return True 

84 return False 

85 

86 

87@functools.cache 

88def get_all_relation_subj_and_obj() -> list[ContentType]: 

89 """ 

90 Return the model classes of any model that is in some way 

91 connected to a relation - either as obj or as subj 

92 

93 Returns: 

94 list[ContentType]: A list of unique ContentTypes for related models. 

95 """ 

96 related_models = set() 

97 for rel in relation_content_types(): 

98 related_models.update(rel.model_class().subj_list()) 

99 related_models.update(rel.model_class().obj_list()) 

100 return [ContentType.objects.get_for_model(item) for item in related_models]