Coverage for apis_core/relations/filtersets.py: 0%

48 statements  

« prev     ^ index     » next       coverage.py v7.5.3, created at 2025-06-25 10:00 +0000

1from django.contrib.contenttypes.models import ContentType 

2from django.db.models import Q 

3from django_filters import CharFilter, MultipleChoiceFilter 

4 

5from apis_core.apis_metainfo.models import RootObject 

6from apis_core.generic.filtersets import GenericFilterSet 

7from apis_core.generic.helpers import generate_search_filter 

8from apis_core.relations.forms import RelationFilterSetForm 

9from apis_core.relations.utils import get_all_relation_subj_and_obj 

10 

11 

12class EntityFilter(CharFilter): 

13 """ 

14 Custom CharFilter that uses the generate_search_filter helper 

15 to search in all instances inheriting from RootObject and then 

16 uses those results to only list relations that point to one of 

17 the results. 

18 """ 

19 

20 def __init__(self, *args, **kwargs): 

21 super().__init__(*args, **kwargs) 

22 self.extra["help_text"] = "Searches in subclasses of RootObject" 

23 

24 def _search_all_entities(self, value) -> list[str]: 

25 q = Q() 

26 for content_type in get_all_relation_subj_and_obj(): 

27 name = content_type.model 

28 q |= Q(**{f"{name}__isnull": False}) & generate_search_filter( 

29 content_type.model_class(), value, prefix=f"{name}__" 

30 ) 

31 return RootObject.objects_inheritance.filter(q).values_list("pk", flat=True) 

32 

33 def filter(self, qs, value): 

34 if value: 

35 all_entities = self._search_all_entities(value) 

36 return qs.filter(**{f"{self.field_name}_object_id__in": all_entities}) 

37 return qs 

38 

39 

40class SubjObjClassFilter(MultipleChoiceFilter): 

41 """ 

42 Custom MultipleChoiceFilter 

43 * it lists model classes as choices, that are in some way 

44 connected to a relation, either as subj or as obj 

45 * it filters relations by the content types of the connected 

46 subjects and objects 

47 """ 

48 

49 def __init__(self, models, *args, **kwargs): 

50 super().__init__(*args, **kwargs) 

51 content_types = [ContentType.objects.get_for_model(model) for model in models] 

52 self.extra["choices"] = [(item.id, item.name) for item in content_types] 

53 

54 def filter(self, qs, value: list[str] | None): 

55 # value is the list of contenttypes ids 

56 if value: 

57 return qs.filter(Q(**{f"{self.field_name}_content_type__in": value})) 

58 return qs 

59 

60 

61class RelationFilterSet(GenericFilterSet): 

62 """ 

63 Override the GenericFilterSet that is created for the Relation model. 

64 It does not really make sense to filter for content type id and object id, 

65 so we exclude those. 

66 Instead, we add a multiple choice filter for object and subject class, that 

67 only lists those choices that actually exists (meaning the classes that are 

68 actually set as subj or obj in some relation). 

69 Additionaly, we add a search filter, that searches in instances connected 

70 to relations (this does only work for instances inheriting from RootObject). 

71 """ 

72 

73 subj_search = EntityFilter( 

74 field_name="subj", 

75 label="Subject search", 

76 ) 

77 obj_search = EntityFilter( 

78 field_name="obj", 

79 label="Object search", 

80 ) 

81 

82 class Meta: 

83 exclude = [ 

84 "subj_object_id", 

85 "subj_content_type", 

86 "obj_object_id", 

87 "obj_content_type", 

88 ] 

89 form = RelationFilterSetForm 

90 

91 def __init__(self, *args, **kwargs): 

92 super().__init__(*args, **kwargs) 

93 if model := getattr(self.Meta, "model", False): 

94 all_models = [ct.model_class() for ct in get_all_relation_subj_and_obj()] 

95 subj_models = getattr(model, "subj_model", all_models) 

96 obj_models = getattr(model, "obj_model", all_models) 

97 if isinstance(subj_models, list): 

98 self.filters["subj_class"] = SubjObjClassFilter( 

99 field_name="subj", label="Subject class", models=subj_models 

100 ) 

101 if isinstance(obj_models, list): 

102 self.filters["obj_class"] = SubjObjClassFilter( 

103 field_name="obj", label="Object Class", models=obj_models 

104 )