Coverage for apis_core/generic/forms/__init__.py: 29%

102 statements  

« prev     ^ index     » next       coverage.py v7.6.10, created at 2025-02-19 16:54 +0000

1import logging 

2 

3from crispy_forms.helper import FormHelper 

4from crispy_forms.layout import Submit 

5from django import forms 

6from django.apps import apps 

7from django.contrib.contenttypes.models import ContentType 

8from django.urls import reverse 

9 

10from apis_core.core.fields import ApisModelSelect2, ApisModelSelect2Multiple 

11from apis_core.generic.abc import GenericModel 

12from apis_core.generic.forms.fields import ModelImportChoiceField 

13 

14logger = logging.getLogger(__name__) 

15 

16 

17class GenericImportForm(forms.Form): 

18 class Meta: 

19 fields = [] 

20 

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

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

23 self.fields["url"] = ModelImportChoiceField( 

24 queryset=self.Meta.model.objects.all() 

25 ) 

26 ct = ContentType.objects.get_for_model(self.Meta.model) 

27 url = reverse("apis_core:generic:autocompleteexternalonly", args=[ct]) 

28 self.fields["url"].widget = ApisModelSelect2( 

29 url, attrs={"data-html": True, "data-tags": 1} 

30 ) 

31 self.fields["url"].widget.choices = self.fields["url"].choices 

32 self.helper = FormHelper() 

33 self.helper.add_input(Submit("submit", "Submit")) 

34 

35 

36class GenericFilterSetForm(forms.Form): 

37 """ 

38 FilterSet form for generic models 

39 Adds a submit button using the django crispy form helper 

40 Adds a `columns` selector that lists all the fields from 

41 the model 

42 """ 

43 

44 columns_exclude = [] 

45 

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

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

48 

49 self.helper = FormHelper() 

50 self.helper.form_method = "GET" 

51 self.helper.add_input(Submit("submit", "Submit")) 

52 

53 def clean(self): 

54 self.cleaned_data = super().clean() 

55 self.cleaned_data.pop("columns", None) 

56 return self.cleaned_data 

57 

58 

59class GenericModelForm(forms.ModelForm): 

60 """ 

61 Model form for generic models 

62 Adds a submit button using the django crispy form helper 

63 and sets the ModelChoiceFields and ModelMultipleChoiceFields 

64 to use autocomplete replacement fields 

65 """ 

66 

67 class Meta: 

68 fields = "__all__" 

69 

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

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

72 try: 

73 skoscollection = apps.get_model("collections.SkosCollection") 

74 self.fields["collections"] = forms.ModelMultipleChoiceField( 

75 required=False, queryset=skoscollection.objects.all() 

76 ) 

77 if instance := kwargs.get("instance"): 

78 self.fields["collections"].initial = skoscollection.objects.by_instance( 

79 instance 

80 ).values_list("pk", flat=True) 

81 except LookupError as e: 

82 logger.debug("Not adding collections to form: %s", e) 

83 

84 self.helper = FormHelper(self) 

85 self.helper.add_input(Submit("submit", "Submit")) 

86 

87 # override the fields pointing to other models, 

88 # to make them use the autocomplete widgets 

89 override_fieldtypes = { 

90 "ModelMultipleChoiceField": ApisModelSelect2Multiple, 

91 "ModelChoiceField": ApisModelSelect2, 

92 "ModelImportChoiceField": ApisModelSelect2, 

93 } 

94 for field in self.fields: 

95 clsname = self.fields[field].__class__.__name__ 

96 if clsname in override_fieldtypes.keys(): 

97 ct = ContentType.objects.get_for_model( 

98 self.fields[field]._queryset.model 

99 ) 

100 if issubclass(ct.model_class(), GenericModel): 

101 url = reverse("apis_core:generic:autocomplete", args=[ct]) 

102 self.fields[field].widget = override_fieldtypes[clsname]( 

103 url, attrs={"data-html": True} 

104 ) 

105 self.fields[field].widget.choices = self.fields[field].choices 

106 

107 def save(self, *args, **kwargs): 

108 instance = super().save(*args, **kwargs) 

109 try: 

110 skoscollection = apps.get_model("collections.SkosCollection") 

111 if collections := self.cleaned_data.get("collections"): 

112 for collection in skoscollection.objects.exclude(pk__in=collections): 

113 collection.remove(instance) 

114 for collection in skoscollection.objects.filter(pk__in=collections): 

115 collection.add(instance) 

116 except LookupError as e: 

117 logger.debug("Not creating collections from form: %s", e) 

118 return instance 

119 

120 

121class GenericSelectMergeOrEnrichForm(forms.Form): 

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

123 if "instance" in kwargs: 

124 instance = kwargs.pop("instance") 

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

126 ct = ContentType.objects.get_for_model(instance) 

127 self.fields["uri"] = forms.ModelChoiceField( 

128 queryset=ct.model_class().objects.all() 

129 ) 

130 uri = reverse("apis_core:generic:autocomplete", args=[ct]) 

131 attrs = {"data-html": True, "data-tags": 1} 

132 self.fields["uri"].widget = ApisModelSelect2(uri, attrs=attrs) 

133 self.fields["uri"].widget.choices = self.fields["uri"].choices 

134 self.fields["uri"].label = "Select or paste URI" 

135 self.helper = FormHelper() 

136 self.helper.form_method = "GET" 

137 self.helper.add_input(Submit("submit", "Submit")) 

138 self.helper.form_action = instance.get_enrich_url() 

139 

140 

141class GenericMergeWithForm(forms.Form): 

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

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

144 self.helper = FormHelper() 

145 self.helper.add_input(Submit("submit", "Merge")) 

146 

147 

148class GenericEnrichForm(forms.Form): 

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

150 data = kwargs.pop("data", {}) 

151 instance = kwargs.pop("instance", None) 

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

153 for key, value in data.items(): 

154 update_key = f"update_{key}" 

155 self.fields[update_key] = forms.BooleanField( 

156 required=False, 

157 label=f"Update {key} from {getattr(instance, key)} to {value}", 

158 ) 

159 

160 self.fields[key] = forms.CharField(initial=value, required=False) 

161 self.fields[key].widget = self.fields[key].hidden_widget() 

162 self.helper = FormHelper() 

163 self.helper.add_input(Submit("submit", "Submit"))