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

103 statements  

« prev     ^ index     » next       coverage.py v7.5.3, created at 2025-06-25 10:00 +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 

9from django.utils.translation import gettext_lazy as _ 

10 

11from apis_core.core.fields import ApisModelSelect2, ApisModelSelect2Multiple 

12from apis_core.generic.abc import GenericModel 

13from apis_core.generic.forms.fields import ModelImportChoiceField 

14 

15logger = logging.getLogger(__name__) 

16 

17 

18class GenericImportForm(forms.Form): 

19 class Meta: 

20 fields = [] 

21 

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

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

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

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

26 ) 

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

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

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

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

31 ) 

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

33 self.helper = FormHelper() 

34 self.helper.add_input(Submit("submit", _("Submit"))) 

35 

36 

37class GenericFilterSetForm(forms.Form): 

38 """ 

39 FilterSet form for generic models 

40 Adds a submit button using the django crispy form helper 

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

42 the model 

43 """ 

44 

45 columns_exclude = [] 

46 

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

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

49 

50 self.helper = FormHelper() 

51 self.helper.form_method = "GET" 

52 self.helper.add_input(Submit("submit", _("Submit"))) 

53 

54 def clean(self): 

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

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

57 return self.cleaned_data 

58 

59 

60class GenericModelForm(forms.ModelForm): 

61 """ 

62 Model form for generic models 

63 Adds a submit button using the django crispy form helper 

64 and sets the ModelChoiceFields and ModelMultipleChoiceFields 

65 to use autocomplete replacement fields 

66 """ 

67 

68 class Meta: 

69 fields = "__all__" 

70 

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

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

73 try: 

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

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

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

77 ) 

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

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

80 instance 

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

82 except LookupError as e: 

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

84 

85 self.helper = FormHelper(self) 

86 self.helper.add_input(Submit("submit", _("Submit"))) 

87 

88 # override the fields pointing to other models, 

89 # to make them use the autocomplete widgets 

90 override_fieldtypes = { 

91 "ModelMultipleChoiceField": ApisModelSelect2Multiple, 

92 "ModelChoiceField": ApisModelSelect2, 

93 "ModelImportChoiceField": ApisModelSelect2, 

94 } 

95 for field in self.fields: 

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

97 if clsname in override_fieldtypes.keys(): 

98 ct = ContentType.objects.get_for_model( 

99 self.fields[field]._queryset.model 

100 ) 

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

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

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

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

105 ) 

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

107 

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

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

110 try: 

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

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

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

114 collection.remove(instance) 

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

116 collection.add(instance) 

117 except LookupError as e: 

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

119 return instance 

120 

121 

122class GenericSelectMergeOrEnrichForm(forms.Form): 

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

124 if "instance" in kwargs: 

125 instance = kwargs.pop("instance") 

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

127 ct = ContentType.objects.get_for_model(instance) 

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

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

130 ) 

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

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

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

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

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

136 self.helper = FormHelper() 

137 self.helper.form_method = "GET" 

138 self.helper.add_input(Submit("submit", _("Submit"))) 

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

140 

141 

142class GenericMergeWithForm(forms.Form): 

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

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

145 self.helper = FormHelper() 

146 self.helper.add_input(Submit("submit", _("Merge"))) 

147 

148 

149class GenericEnrichForm(forms.Form): 

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

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

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

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

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

155 update_key = f"update_{key}" 

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

157 required=False, 

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

159 ) 

160 

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

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

163 self.helper = FormHelper() 

164 self.helper.add_input(Submit("submit", _("Submit")))