Coverage for apis_core/generic/forms/__init__.py: 30%
104 statements
« prev ^ index » next coverage.py v7.5.3, created at 2025-09-03 06:15 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2025-09-03 06:15 +0000
1import logging
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 _
11from apis_core.core.fields import ApisModelSelect2, ApisModelSelect2Multiple
12from apis_core.generic.abc import GenericModel
13from apis_core.generic.forms.fields import ModelImportChoiceField
15logger = logging.getLogger(__name__)
18class GenericImportForm(forms.Form):
19 class Meta:
20 fields = []
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")))
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 """
45 columns_exclude = []
47 def __init__(self, *args, **kwargs):
48 super().__init__(*args, **kwargs)
50 self.helper = FormHelper(self)
51 self.helper.form_method = "GET"
52 self.helper.add_input(Submit("submit", _("Submit")))
54 def clean(self):
55 self.cleaned_data = super().clean()
56 self.cleaned_data.pop("columns", None)
57 return self.cleaned_data
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 """
68 class Meta:
69 fields = "__all__"
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,
77 queryset=skoscollection.objects.all(),
78 label=_("Collections"),
79 )
80 if instance := kwargs.get("instance"):
81 self.fields["collections"].initial = skoscollection.objects.by_instance(
82 instance
83 ).values_list("pk", flat=True)
84 except LookupError as e:
85 logger.debug("Not adding collections to form: %s", e)
87 self.helper = FormHelper(self)
88 self.helper.add_input(Submit("submit", _("Submit")))
90 # override the fields pointing to other models,
91 # to make them use the autocomplete widgets
92 override_fieldtypes = {
93 "ModelMultipleChoiceField": ApisModelSelect2Multiple,
94 "ModelChoiceField": ApisModelSelect2,
95 "ModelImportChoiceField": ApisModelSelect2,
96 }
97 for field in self.fields:
98 clsname = self.fields[field].__class__.__name__
99 if clsname in override_fieldtypes.keys():
100 ct = ContentType.objects.get_for_model(
101 self.fields[field]._queryset.model
102 )
103 if issubclass(ct.model_class(), GenericModel):
104 url = reverse("apis_core:generic:autocomplete", args=[ct])
105 self.fields[field].widget = override_fieldtypes[clsname](
106 url, attrs={"data-html": True}
107 )
108 self.fields[field].widget.choices = self.fields[field].choices
110 def save(self, *args, **kwargs):
111 instance = super().save(*args, **kwargs)
112 try:
113 skoscollection = apps.get_model("collections.SkosCollection")
114 if "collections" in self.cleaned_data:
115 collections = self.cleaned_data.get("collections")
116 for collection in skoscollection.objects.exclude(pk__in=collections):
117 collection.remove(instance)
118 for collection in skoscollection.objects.filter(pk__in=collections):
119 collection.add(instance)
120 except LookupError as e:
121 logger.debug("Not creating collections from form: %s", e)
122 return instance
125class GenericSelectMergeOrEnrichForm(forms.Form):
126 def __init__(self, *args, **kwargs):
127 if "instance" in kwargs:
128 instance = kwargs.pop("instance")
129 super().__init__(*args, **kwargs)
130 ct = ContentType.objects.get_for_model(instance)
131 self.fields["uri"] = forms.ModelChoiceField(
132 queryset=ct.model_class().objects.all()
133 )
134 uri = reverse("apis_core:generic:autocomplete", args=[ct])
135 attrs = {"data-html": True, "data-tags": 1}
136 self.fields["uri"].widget = ApisModelSelect2(uri, attrs=attrs)
137 self.fields["uri"].widget.choices = self.fields["uri"].choices
138 self.fields["uri"].label = "Select or paste URI"
139 self.helper = FormHelper()
140 self.helper.form_method = "GET"
141 self.helper.add_input(Submit("submit", _("Submit")))
142 self.helper.form_action = instance.get_enrich_url()
145class GenericMergeWithForm(forms.Form):
146 def __init__(self, *args, **kwargs):
147 super().__init__(*args, **kwargs)
148 self.helper = FormHelper()
149 self.helper.add_input(Submit("submit", _("Merge")))
152class GenericEnrichForm(forms.Form):
153 def __init__(self, *args, **kwargs):
154 data = kwargs.pop("data", {})
155 instance = kwargs.pop("instance", None)
156 super().__init__(*args, **kwargs)
157 for key, value in data.items():
158 update_key = f"update_{key}"
159 self.fields[update_key] = forms.BooleanField(
160 required=False,
161 label=f"Update {key} from {getattr(instance, key)} to {value}",
162 )
164 self.fields[key] = forms.CharField(initial=value, required=False)
165 self.fields[key].widget = self.fields[key].hidden_widget()
166 self.helper = FormHelper()
167 self.helper.add_input(Submit("submit", _("Submit")))