diff --git a/select_url_field/admin_urls.py b/select_url_field/admin_urls.py
new file mode 100644
index 0000000..499b49c
--- /dev/null
+++ b/select_url_field/admin_urls.py
@@ -0,0 +1,10 @@
+from django.urls import re_path
+from select_url_field.admin_views import select_url_choices
+
+urlpatterns = [
+ re_path(
+ r"^$",
+ select_url_choices,
+ name="select_url_choices",
+ ),
+]
diff --git a/select_url_field/admin_views.py b/select_url_field/admin_views.py
new file mode 100644
index 0000000..33fb84b
--- /dev/null
+++ b/select_url_field/admin_views.py
@@ -0,0 +1,26 @@
+import json
+from importlib import import_module
+
+from django.conf import settings
+from django.contrib.admin.views.decorators import staff_member_required
+from django.http import HttpResponse
+
+
+@staff_member_required
+def select_url_choices(request):
+ mod_path, func_name = settings.SELECT_URL_CHOICES_FUNC.rsplit(".", 1)
+ mod = import_module(mod_path)
+ choices_func = getattr(mod, func_name)
+ choices = []
+ try:
+ choices_data = choices_func()
+ for item in choices_data:
+ choice_group = {
+ "group_name": item[0],
+ "group_choices": [[x[1], x[0]] for x in item[1]],
+ }
+ choices.append(choice_group)
+ except (ValueError, IndexError, TypeError):
+ pass
+
+ return HttpResponse(json.dumps(choices), content_type="application/json")
diff --git a/select_url_field/choice_with_other.py b/select_url_field/choice_with_other.py
index ddd3589..048e5ea 100644
--- a/select_url_field/choice_with_other.py
+++ b/select_url_field/choice_with_other.py
@@ -1,75 +1,67 @@
from django import forms
-from django.conf import settings
-
-OTHER_CHOICE = '__other__'
-OTHER_CHOICE_DISPLAY = '' # 'Other:'
+OTHER_CHOICE = "__other__"
+OTHER_CHOICE_DISPLAY = "" # 'Other:'
class ChoiceWithOtherWidget(forms.MultiWidget):
-
"""MultiWidget for use with ChoiceWithOtherField"""
-
+
+ template_name = "admin/widgets/choice_with_other.html"
+
def __init__(self, choices, attrs=None):
- widgets = [
- forms.Select(choices=choices),
- forms.TextInput(attrs={'size': '80'})
- ]
+ widgets = [forms.Select(choices=choices), forms.TextInput(attrs={"size": "80"})]
self.choices = choices
super(ChoiceWithOtherWidget, self).__init__(widgets, attrs=attrs)
def decompress(self, value):
if value:
choices = [c[0] for c in self.choices]
- provided_choices, other_choice = choices[:-1], choices[-1:]
+ provided_choices, _ = choices[:-1], choices[-1:]
if value in provided_choices:
- return [value, '']
+ return [value, ""]
else:
return [OTHER_CHOICE, value]
- return ['', '']
-
- def format_output(self, rendered_widgets):
-
- """Format the output by substituting the "other" choice into the first widget"""
-
- return u'
{} {}
'.format(
- rendered_widgets[0],
- rendered_widgets[1],
- )
+ return ["", ""]
def _media(self):
- return forms.Media(js=('admin/choice_with_other.js',))
+ return forms.Media(js=("admin/choice_with_other.js",))
+
media = property(_media)
class ChoiceWithOtherField(forms.MultiValueField):
-
def __init__(self, *args, **kwargs):
- choices = list(kwargs.pop('choices'))
+ choices = list(kwargs.pop("choices"))
has_empty_choice = False
for c in choices:
if not c[0]:
has_empty_choice = True
break
if not has_empty_choice:
- choices.insert(0, ('', '---------'))
+ choices.insert(0, ("", "---------"))
choices.append((OTHER_CHOICE, OTHER_CHOICE_DISPLAY))
- fields = [
- forms.ChoiceField(choices=choices),
- forms.CharField(required=False)
- ]
+ fields = [forms.ChoiceField(choices=choices), forms.CharField(required=False)]
widget = ChoiceWithOtherWidget(choices=choices)
- self._was_required = kwargs.pop('required', True)
- kwargs['required'] = False
+ self._was_required = kwargs.pop("required", True)
+ kwargs["required"] = False
super(ChoiceWithOtherField, self).__init__(widget=widget, fields=fields, *args, **kwargs)
def compress(self, value):
- if self._was_required and (not value or value[0] in (None, '')):
- raise forms.ValidationError(self.error_messages['required'])
+ if self._was_required and (not value or value[0] in (None, "")):
+ raise forms.ValidationError(self.error_messages["required"])
if not value:
- return ''
-
+ return ""
+
# value[0] is ignored. Only ever take the value from the text field.
# The select menu is just a way to correctly fill in the text field.
return value[1]
+
+class AjaxSelectURLWidget(forms.TextInput):
+ """Plain text input that upgrades itself to a grouped