Coverage for apis_core/generic/forms/__init__.py: 83%
115 statements
« prev ^ index » next coverage.py v7.5.3, created at 2025-10-10 13:36 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2025-10-10 13:36 +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.core.exceptions import ValidationError
9from django.urls import reverse
10from django.utils.translation import gettext_lazy as _
12from apis_core.core.fields import (
13 ApisListSelect2,
14 ApisModelSelect2,
15 ApisModelSelect2Multiple,
16)
17from apis_core.generic.abc import GenericModel
18from apis_core.generic.forms.fields import ModelImportChoiceField
20logger = logging.getLogger(__name__)
23class GenericImportForm(forms.Form):
24 class Meta:
25 fields = []
27 def __init__(self, *args, **kwargs):
28 super().__init__(*args, **kwargs)
29 self.fields["url"] = ModelImportChoiceField(
30 queryset=self.Meta.model.objects.all()
31 )
32 ct = ContentType.objects.get_for_model(self.Meta.model)
33 url = reverse("apis_core:generic:autocompleteexternalonly", args=[ct])
34 self.fields["url"].widget = ApisModelSelect2(
35 url, attrs={"data-html": True, "data-tags": 1}
36 )
37 self.fields["url"].widget.choices = self.fields["url"].choices
38 self.helper = FormHelper()
39 self.helper.add_input(Submit("submit", _("Submit")))
42class GenericFilterSetForm(forms.Form):
43 """
44 FilterSet form for generic models
45 Adds a submit button using the django crispy form helper
46 Adds a `columns` selector that lists all the fields from
47 the model
48 """
50 columns_exclude = []
52 def __init__(self, *args, **kwargs):
53 super().__init__(*args, **kwargs)
55 self.helper = FormHelper(self)
56 self.helper.form_method = "GET"
57 self.helper.add_input(Submit("submit", _("Submit")))
59 def clean(self):
60 self.cleaned_data = super().clean()
61 self.cleaned_data.pop("columns", None)
62 return self.cleaned_data
65class GenericModelForm(forms.ModelForm):
66 """
67 Model form for generic models
68 Adds a submit button using the django crispy form helper
69 and sets the ModelChoiceFields and ModelMultipleChoiceFields
70 to use autocomplete replacement fields
71 """
73 class Meta:
74 fields = "__all__"
76 def __init__(self, *args, **kwargs):
77 super().__init__(*args, **kwargs)
78 try:
79 skoscollection = apps.get_model("collections.SkosCollection")
80 self.fields["collections"] = forms.ModelMultipleChoiceField(
81 required=False,
82 queryset=skoscollection.objects.all(),
83 label=_("Collections"),
84 )
85 if instance := kwargs.get("instance"):
86 self.fields["collections"].initial = skoscollection.objects.by_instance(
87 instance
88 ).values_list("pk", flat=True)
89 except LookupError as e:
90 logger.debug("Not adding collections to form: %s", e)
92 self.helper = FormHelper(self)
93 self.helper.add_input(Submit("submit", _("Submit")))
95 # override the fields pointing to other models,
96 # to make them use the autocomplete widgets
97 override_fieldtypes = {
98 "ModelMultipleChoiceField": ApisModelSelect2Multiple,
99 "ModelChoiceField": ApisModelSelect2,
100 "ModelImportChoiceField": ApisModelSelect2,
101 }
102 for field in self.fields:
103 clsname = self.fields[field].__class__.__name__
104 if clsname in override_fieldtypes.keys():
105 ct = ContentType.objects.get_for_model(
106 self.fields[field]._queryset.model
107 )
108 if issubclass(ct.model_class(), GenericModel):
109 url = reverse("apis_core:generic:autocomplete", args=[ct])
110 self.fields[field].widget = override_fieldtypes[clsname](
111 url, attrs={"data-html": True}
112 )
113 self.fields[field].widget.choices = self.fields[field].choices
115 def clean(self):
116 cleaned_data = super().clean()
117 if not any(cleaned_data.values()):
118 raise ValidationError(_("Please fill out some of the form fields"))
119 return cleaned_data
121 def save(self, *args, **kwargs):
122 instance = super().save(*args, **kwargs)
123 try:
124 skoscollection = apps.get_model("collections.SkosCollection")
125 if "collections" in self.cleaned_data:
126 collections = self.cleaned_data.get("collections")
127 for collection in skoscollection.objects.exclude(pk__in=collections):
128 collection.remove(instance)
129 for collection in skoscollection.objects.filter(pk__in=collections):
130 collection.add(instance)
131 except LookupError as e:
132 logger.debug("Not creating collections from form: %s", e)
133 return instance
136class GenericSelectMergeOrEnrichForm(forms.Form):
137 def __init__(self, *args, **kwargs):
138 if "content_type" in kwargs:
139 self.content_type = kwargs.pop("content_type")
140 super().__init__(*args, **kwargs)
141 self.fields["uri"] = forms.CharField()
142 uri = reverse("apis_core:generic:autocomplete", args=[self.content_type])
143 attrs = {"data-html": True, "data-tags": 1}
144 self.fields["uri"].widget = ApisListSelect2(uri, attrs=attrs)
145 self.fields["uri"].label = "Select or paste URI"
146 self.helper = FormHelper()
147 self.helper.add_input(Submit("submit", _("Submit")))
149 def clean_uri(self):
150 uri = self.cleaned_data["uri"]
151 if uri.isdigit() or self.content_type.model_class().valid_uri(uri):
152 return uri
153 raise ValueError(f"{uri} is neither an ID nor something we can import")
156class GenericMergeWithForm(forms.Form):
157 def __init__(self, *args, **kwargs):
158 super().__init__(*args, **kwargs)
159 self.helper = FormHelper()
160 self.helper.add_input(Submit("submit", _("Merge")))
163class GenericEnrichForm(forms.Form):
164 def __init__(self, *args, **kwargs):
165 data = kwargs.pop("data", {})
166 instance = kwargs.pop("instance", None)
167 super().__init__(*args, **kwargs)
168 for key, value in data.items():
169 update_key = f"update_{key}"
170 if value:
171 label = f"Add {key} {value}"
172 if existing := getattr(instance, key, False):
173 label = f"Update {key} from {existing} to {value}"
174 self.fields[update_key] = forms.BooleanField(
175 required=False, label=label
176 )
178 self.fields[key] = forms.CharField(initial=value, required=False)
179 self.fields[key].widget = self.fields[key].hidden_widget()
180 self.helper = FormHelper()
181 self.helper.add_input(Submit("submit", _("Submit")))