From 7ab88c2ed3252f44702b42403ab847b908c8421e Mon Sep 17 00:00:00 2001 From: Aakash Kumar Das Date: Tue, 5 Apr 2022 00:37:56 +0530 Subject: [PATCH 01/12] Update fields.py --- select_url_field/fields.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/select_url_field/fields.py b/select_url_field/fields.py index 9abaee9..a3363fa 100644 --- a/select_url_field/fields.py +++ b/select_url_field/fields.py @@ -5,8 +5,8 @@ from django.core.exceptions import ValidationError from django.core.validators import URLValidator from django.db import models -from django.utils.translation import ugettext_lazy as _ -from django.utils.encoding import smart_unicode +from django.utils.translation import gettext_lazy as _ +from django.utils.encoding import smart_str as smart_unicode from select_url_field import select_url_field_settings try: From 5109bfe6b9175489a0ca07124cb3cd70b37f602f Mon Sep 17 00:00:00 2001 From: Aakash Kumar Das Date: Tue, 5 Apr 2022 00:39:03 +0530 Subject: [PATCH 02/12] Update fields.py --- select_url_field/fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/select_url_field/fields.py b/select_url_field/fields.py index a3363fa..48d8c8a 100644 --- a/select_url_field/fields.py +++ b/select_url_field/fields.py @@ -83,7 +83,7 @@ def __call__(self, value): try: # OK if it's a valid url self.url_validator(value) - except ValidationError, e: + except ValidationError as e: # Not a valid url, see it's a path if not self.regex.search(smart_unicode(value)): raise e From 3865cbc63adabf1197a4849e03344b07cf674112 Mon Sep 17 00:00:00 2001 From: Chen Zhe Date: Wed, 3 Aug 2022 15:09:48 +0800 Subject: [PATCH 03/12] fix js error --- .../static/admin/choice_with_other.js | 64 ++++++++++--------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/select_url_field/static/admin/choice_with_other.js b/select_url_field/static/admin/choice_with_other.js index 4d06525..459bf7a 100644 --- a/select_url_field/static/admin/choice_with_other.js +++ b/select_url_field/static/admin/choice_with_other.js @@ -1,35 +1,37 @@ -(function($) { - var OTHER_CHOICE = '__other__'; //See - $(function(){ - $('body').delegate('.choice_with_other_wrapper select', 'change', function(e){ - var select = $(this) - var text_input = $(this).parent().find('input') - if(select.val() == OTHER_CHOICE){ - ; - }else{ - text_input.val(select.val()); - } - }); - var text_input = $('input', this); - $('body').delegate('.choice_with_other_wrapper input', 'keyup', function(e){ - var match = false; - var text_input = $(this) - var select = $(this).parent().find('select') - $(select).find('option').each(function(){ - if($(this).attr('value') == text_input.val()){ - match = true; +window.addEventListener("load", function() { + (function($) { + var OTHER_CHOICE = '__other__'; //See + $(function(){ + $('body').delegate('.choice_with_other_wrapper select', 'change', function(e){ + var select = $(this) + var text_input = $(this).parent().find('input') + if(select.val() == OTHER_CHOICE){ + ; + }else{ + text_input.val(select.val()); + } + }); + var text_input = $('input', this); + $('body').delegate('.choice_with_other_wrapper input', 'keyup', function(e){ + var match = false; + var text_input = $(this) + var select = $(this).parent().find('select') + $(select).find('option').each(function(){ + if($(this).attr('value') == text_input.val()){ + match = true; + } + }) + if(!match){ + select.val(OTHER_CHOICE); + }else{ + select.val(text_input.val()); } }) - if(!match){ - select.val(OTHER_CHOICE); - }else{ - select.val(text_input.val()); - } - }) - $('.choice_with_other_wrapper').each(function(){ - $('select', this).change(); - }) + $('.choice_with_other_wrapper').each(function(){ + $('select', this).change(); + }) - }) -})(django.jQuery); \ No newline at end of file + }) + })(django.jQuery); +}); From 38011a9807e7e9617115dd1a0c2c44156718e0c1 Mon Sep 17 00:00:00 2001 From: Chen Zhe Date: Wed, 3 Aug 2022 16:31:47 +0800 Subject: [PATCH 04/12] upgrade to django4 --- select_url_field/choice_with_other.py | 11 +++-------- .../templates/admin/widgets/choice_with_other.html | 2 ++ 2 files changed, 5 insertions(+), 8 deletions(-) create mode 100644 select_url_field/templates/admin/widgets/choice_with_other.html diff --git a/select_url_field/choice_with_other.py b/select_url_field/choice_with_other.py index ddd3589..7b86ece 100644 --- a/select_url_field/choice_with_other.py +++ b/select_url_field/choice_with_other.py @@ -9,6 +9,8 @@ class ChoiceWithOtherWidget(forms.MultiWidget): """MultiWidget for use with ChoiceWithOtherField""" + + template_name = 'admin/widgets/choice_with_other.html' def __init__(self, choices, attrs=None): widgets = [ @@ -28,14 +30,7 @@ def decompress(self, value): 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], - ) + def _media(self): return forms.Media(js=('admin/choice_with_other.js',)) 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..da02ded --- /dev/null +++ b/select_url_field/templates/admin/widgets/choice_with_other.html @@ -0,0 +1,2 @@ +{% spaceless %}
{% for widget in widget.subwidgets %}{% include widget.template_name %}{% endfor %}
{% endspaceless %} + From cd0434956a9c6d2c2fcf389790ea7f593908c42d Mon Sep 17 00:00:00 2001 From: Chen Zhe Date: Thu, 28 Dec 2023 18:03:19 +0800 Subject: [PATCH 05/12] remove custom field code for south --- select_url_field/fields.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/select_url_field/fields.py b/select_url_field/fields.py index 48d8c8a..03784e1 100644 --- a/select_url_field/fields.py +++ b/select_url_field/fields.py @@ -98,8 +98,3 @@ def __init__(self, max_length=None, min_length=None, *args, **kwargs): super(SelectURLFormField, self).__init__(max_length, min_length, *args, **kwargs) self.validators.append(SelectURLValidator()) - -if 'south' in settings.INSTALLED_APPS: - from south.modelsinspector import add_introspection_rules - add_introspection_rules([], ["^select_url_field\.fields\.IxxyURLField"]) - add_introspection_rules([], ["^select_url_field\.fields\.SelectURLField"]) From 62af9bbccb3f2247937234f69398435ceaa5e969 Mon Sep 17 00:00:00 2001 From: James Rampton Date: Mon, 22 Sep 2025 09:47:35 +0100 Subject: [PATCH 06/12] Formatting and lint fixes --- select_url_field/choice_with_other.py | 50 +++++++------------ select_url_field/fields.py | 31 ++++++------ select_url_field/select_url_field_settings.py | 2 +- 3 files changed, 34 insertions(+), 49 deletions(-) diff --git a/select_url_field/choice_with_other.py b/select_url_field/choice_with_other.py index 7b86ece..067a20d 100644 --- a/select_url_field/choice_with_other.py +++ b/select_url_field/choice_with_other.py @@ -1,70 +1,58 @@ 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' - + 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 ['', ''] - - + 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] - diff --git a/select_url_field/fields.py b/select_url_field/fields.py index 03784e1..4c9011f 100644 --- a/select_url_field/fields.py +++ b/select_url_field/fields.py @@ -5,8 +5,8 @@ from django.core.exceptions import ValidationError from django.core.validators import URLValidator from django.db import models -from django.utils.translation import gettext_lazy as _ from django.utils.encoding import smart_str as smart_unicode +from django.utils.translation import gettext_lazy as _ from select_url_field import select_url_field_settings try: @@ -23,22 +23,22 @@ class SelectURLField(models.CharField): description = _("URL") def __init__(self, verbose_name=None, name=None, **kwargs): - kwargs['max_length'] = kwargs.get('max_length', 200) + kwargs["max_length"] = kwargs.get("max_length", 200) # Handle choices option: # from custom_site.url_choices import get_url_choices # link = SelectURLField(blank=True, choices=get_url_choices) self._has_choices = False - if 'choices' in kwargs: + if "choices" in kwargs: self._has_choices = True - self._url_choices = kwargs.pop('choices') - + self._url_choices = kwargs.pop("choices") + models.CharField.__init__(self, verbose_name, name, **kwargs) self.validators.append(SelectURLValidator()) def formfield(self, **kwargs): # As with CharField, this will cause URL validation to be performed twice defaults = { - 'form_class': SelectURLFormField, + "form_class": SelectURLFormField, } defaults.update(kwargs) # When choices given, use them @@ -49,7 +49,7 @@ def formfield(self, **kwargs): else: choices = self._url_choices else: - mod_path, func_name = select_url_field_settings.URL_CHOICES_FUNC.rsplit('.', 1) + mod_path, func_name = select_url_field_settings.URL_CHOICES_FUNC.rsplit(".", 1) mod = import_module(mod_path) choices_func = getattr(mod, func_name) choices = choices_func() @@ -57,13 +57,12 @@ def formfield(self, **kwargs): return ChoiceWithOtherField(choices=choices, required=required) def to_python(self, value): - from django.conf import settings if value: - domain = getattr(settings, 'SITE_DOMAIN', '') + domain = getattr(settings, "SITE_DOMAIN", "") if domain: - domain_pattern = r'^(?:http|ftp)s?://' + domain + domain_pattern = r"^(?:http|ftp)s?://" + domain domain_regex = re.compile(domain_pattern, re.IGNORECASE) - value = domain_regex.sub('', value) + value = domain_regex.sub("", value) return super(SelectURLField, self).to_python(value) @@ -72,13 +71,12 @@ def to_python(self, value): class SelectURLValidator(object): - - code = 'invalid' - regex = re.compile(r'(?:[/?]\S+)$', re.IGNORECASE) + code = "invalid" + regex = re.compile(r"(?:[/?]\S+)$", re.IGNORECASE) def __init__(self): self.url_validator = URLValidator() - + def __call__(self, value): try: # OK if it's a valid url @@ -91,10 +89,9 @@ def __call__(self, value): class SelectURLFormField(forms.CharField): default_error_messages = { - 'invalid': _(u'Enter a valid URL.'), + "invalid": _("Enter a valid URL."), } def __init__(self, max_length=None, min_length=None, *args, **kwargs): super(SelectURLFormField, self).__init__(max_length, min_length, *args, **kwargs) self.validators.append(SelectURLValidator()) - diff --git a/select_url_field/select_url_field_settings.py b/select_url_field/select_url_field_settings.py index 3ae4d0b..b1ee3bb 100644 --- a/select_url_field/select_url_field_settings.py +++ b/select_url_field/select_url_field_settings.py @@ -1,3 +1,3 @@ from django.conf import settings -URL_CHOICES_FUNC = getattr(settings, 'SELECT_URL_CHOICES_FUNC', 'choices_func.get_url_choices') \ No newline at end of file +URL_CHOICES_FUNC = getattr(settings, "SELECT_URL_CHOICES_FUNC", "choices_func.get_url_choices") From e2fd2b763d557addd45304e5100d207e16fc837b Mon Sep 17 00:00:00 2001 From: James Rampton Date: Tue, 23 Sep 2025 11:29:57 +0100 Subject: [PATCH 07/12] Formatting --- .../templates/admin/widgets/choice_with_other.html | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/select_url_field/templates/admin/widgets/choice_with_other.html b/select_url_field/templates/admin/widgets/choice_with_other.html index da02ded..8777eff 100644 --- a/select_url_field/templates/admin/widgets/choice_with_other.html +++ b/select_url_field/templates/admin/widgets/choice_with_other.html @@ -1,2 +1,8 @@ -{% spaceless %}
{% for widget in widget.subwidgets %}{% include widget.template_name %}{% endfor %}
{% endspaceless %} +{% spaceless %} +
+ {% for widget in widget.subwidgets %} + {% include widget.template_name %} + {% endfor %} +
+{% endspaceless %} From 5fd36c1349d7996df8058998e05cf263c5b2d8f2 Mon Sep 17 00:00:00 2001 From: Chen Zhe Date: Sun, 5 Oct 2025 13:16:18 +0800 Subject: [PATCH 08/12] add new field AjaxSelectURLField --- select_url_field/choice_with_other.py | 9 +++ select_url_field/fields.py | 27 ++++++- .../static/admin/ajax_url_select_field.js | 73 +++++++++++++++++++ .../admin/widgets/ajax_select_url_field.html | 5 ++ 4 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 select_url_field/static/admin/ajax_url_select_field.js create mode 100644 select_url_field/templates/admin/widgets/ajax_select_url_field.html diff --git a/select_url_field/choice_with_other.py b/select_url_field/choice_with_other.py index 067a20d..097e7a7 100644 --- a/select_url_field/choice_with_other.py +++ b/select_url_field/choice_with_other.py @@ -56,3 +56,12 @@ def compress(self, value): # 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 %} From 4dfc12d9a668393a5af229777e7a098c68acba00 Mon Sep 17 00:00:00 2001 From: Chen Zhe Date: Sun, 5 Oct 2025 13:18:21 +0800 Subject: [PATCH 09/12] remove debug output --- select_url_field/static/admin/ajax_url_select_field.js | 1 - 1 file changed, 1 deletion(-) diff --git a/select_url_field/static/admin/ajax_url_select_field.js b/select_url_field/static/admin/ajax_url_select_field.js index dd93d13..1189eb3 100644 --- a/select_url_field/static/admin/ajax_url_select_field.js +++ b/select_url_field/static/admin/ajax_url_select_field.js @@ -39,7 +39,6 @@ $input.addClass('upgraded'); $input.attr({"size": "80"}); var currentVal = $input.val(); - console.log(currentVal) getChoices().done(function (data) { var $select = buildSelect(data, currentVal); From 7bf442c94437a4343ecb2bf9946179583693728f Mon Sep 17 00:00:00 2001 From: Chen Zhe Date: Sun, 5 Oct 2025 16:31:57 +0800 Subject: [PATCH 10/12] select __other__ when we found no match in all the options. --- .../static/admin/ajax_url_select_field.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/select_url_field/static/admin/ajax_url_select_field.js b/select_url_field/static/admin/ajax_url_select_field.js index 1189eb3..a640a4b 100644 --- a/select_url_field/static/admin/ajax_url_select_field.js +++ b/select_url_field/static/admin/ajax_url_select_field.js @@ -14,22 +14,31 @@ function buildSelect(data, currentVal) { var $sel = $('