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 out of the JSON */ + function buildSelect(data, currentVal) { + var $sel = $(' + +{% endspaceless %} diff --git a/select_url_field/templates/admin/widgets/choice_with_other.html b/select_url_field/templates/admin/widgets/choice_with_other.html new file mode 100644 index 0000000..8777eff --- /dev/null +++ b/select_url_field/templates/admin/widgets/choice_with_other.html @@ -0,0 +1,8 @@ +{% spaceless %} +
+ {% for widget in widget.subwidgets %} + {% include widget.template_name %} + {% endfor %} +
+{% endspaceless %} +