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

35 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-09-16 07:42 +0000

1from django.db.models import Q 

2from django_filters import CharFilter, MultipleChoiceFilter 

3 

4from apis_core.apis_metainfo.models import RootObject 

5from apis_core.generic.filtersets import GenericFilterSet 

6from apis_core.generic.helpers import generate_search_filter 

7from apis_core.relations.utils import get_all_relation_subj_and_obj 

8 

9 

10class EntityFilter(CharFilter): 

11 """ 

12 Custom CharFilter that uses the generate_search_filter helper 

13 to search in all instances inheriting from RootObject and then 

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

15 the results. 

16 """ 

17 

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

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

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

21 

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

23 q = Q() 

24 for content_type in get_all_relation_subj_and_obj(): 

25 name = content_type.model 

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

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

28 ) 

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

30 

31 def filter(self, qs, value): 

32 all_entities = self._search_all_entities(value) 

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

34 

35 

36class SubjObjClassFilter(MultipleChoiceFilter): 

37 """ 

38 Custom MultipleChoiceFilter 

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

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

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

42 subjects and objects 

43 """ 

44 

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

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

47 self.extra["choices"] = [ 

48 (item.id, item.name) for item in get_all_relation_subj_and_obj() 

49 ] 

50 

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

52 # value is the list of contenttypes ids 

53 if value: 

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

55 return qs 

56 

57 

58class RelationFilterSet(GenericFilterSet): 

59 """ 

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

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

62 so we exclude those. 

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

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

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

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

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

68 """ 

69 

70 subj_search = EntityFilter( 

71 field_name="subj", 

72 label="Subject search", 

73 ) 

74 obj_search = EntityFilter( 

75 field_name="obj", 

76 label="Object search", 

77 ) 

78 subj_class = SubjObjClassFilter( 

79 field_name="subj", 

80 label="Subject class", 

81 ) 

82 obj_class = SubjObjClassFilter( 

83 field_name="obj", 

84 label="Object class", 

85 ) 

86 

87 class Meta: 

88 exclude = [ 

89 "subj_object_id", 

90 "subj_content_type", 

91 "obj_object_id", 

92 "obj_content_type", 

93 ] 

94 form = GenericFilterSet.Meta.form