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