Coverage for apis_core/relations/utils.py: 54%
35 statements
« prev ^ index » next coverage.py v7.5.3, created at 2025-10-10 13:36 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2025-10-10 13:36 +0000
1import functools
3from django.contrib.contenttypes.models import ContentType
5from apis_core.relations.models import Relation
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 )
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
32 is contenttype.model_class().subj_model_type(),
33 relationcts,
34 )
35 )
36 if obj_model is not None:
37 relationcts = list(
38 filter(
39 lambda contenttype: obj_model
40 is contenttype.model_class().obj_model_type(),
41 relationcts,
42 )
43 )
44 if any_model is not None:
45 relationcts = list(
46 filter(
47 lambda contenttype: any_model
48 is contenttype.model_class().obj_model_type()
49 or any_model is contenttype.model_class().subj_model_type(),
50 relationcts,
51 )
52 )
53 if all(combination):
54 left, right = combination
55 rels = list(
56 filter(
57 lambda contenttype: right is contenttype.model_class().obj_model_type()
58 and left is contenttype.model_class().subj_model_type(),
59 relationcts,
60 )
61 )
62 rels.extend(
63 list(
64 filter(
65 lambda contenttype: left
66 is contenttype.model_class().obj_model_type()
67 and right is contenttype.model_class().subj_model_type(),
68 relationcts,
69 )
70 )
71 )
72 relationcts = rels
73 return set(relationcts)
76def relation_match_target(relation, target: ContentType) -> bool:
77 """
78 test if a relation points to a target
79 this function should not be cached, because the `forward` attribute
80 is an annotation that does not seem to be part of the relation, so
81 if cached, method could be called with another `forward` value and
82 return the wrong result
83 """
84 if relation.forward and relation.obj_content_type == target:
85 return True
86 if not relation.forward and relation.subj_content_type == target:
87 return True
88 return False
91@functools.cache
92def get_all_relation_subj_and_obj() -> list[ContentType]:
93 """
94 Return the model classes of any model that is in some way
95 connected to a relation - either as obj or as subj
97 Returns:
98 list[ContentType]: A list of unique ContentTypes for related models.
99 """
100 related_models = set()
101 for rel in relation_content_types():
102 related_models.add(rel.model_class().subj_model_type())
103 related_models.add(rel.model_class().obj_model_type())
104 return [ContentType.objects.get_for_model(item) for item in related_models]