Свободный текст в Select2ListView django-autocomplete-light
Я изложу свой случай как можно более подробно:
Я использую функциональность автозаполнения для автоматического заполнения формы динамическим значением. Форма имеет несколько значений, при заполнении первых трех я могу запустить операцию (с этими значениями) и вернуть пользователю предложенное значение. Мне нужно было немного адаптировать возврат параметров, но я достиг этого с помощью Select2ListView
class MyFunctionAutocomplete(autocomplete.Select2ListView):
def get_list(self):
if not self.request.user.is_authenticated:
return [""]
try:
field1_pk = self.forwarded["field1"]
field2_pk = self.forwarded["field2"]
field3_pk = self.forwarded["field3"]
except AttributeError:
return [""]
try:
ObjField1 = ModelField1.objects.get(pk=field1_pk)
ObjField2 = ModelField2.objects.get(pk=field2_pk)
ObjField3 = ModelField3.objects.get(pk=field3_pk)
return_value = ObjField1.get_result(param1=ObjField2, param2=ObjField3)
except (ModelField1.DoesNotExist, ModelField2.DoesNotExist, ModelField3.DoesNotExist):
return [""]
return [return_value]
С помощью этого кода я могу делать то, что мне нужно, когда присутствуют требования (пересылаемое значение). Пока все хорошо
Моя проблема: Я не могу ДОБАВИТЬ какое-либо значение в качестве дополнительной опции. Это значение НЕ является ForeignKey, это простой Float. Я хочу, чтобы пользователь мог использовать предложенное мной значение или просто заменить его другим значением по своему выбору. Допустим, в выпадающем списке у них есть значение 3.22221, а они хотят написать 12.11.
Поскольку это может быть практически ЛЮБОЕ значение, я подумал, что свободный текст имеет смысл (я позабочусь об очистке данных, если потребуется, позже). Я проверил и похоже, что https://select2.org/tagging - это то, что мне нужно, но не уверен, могу ли я использовать его здесь.
Мое поле в форме, если это поможет, выглядит так:
result_field = Select2ListCreateChoiceField(
widget=autocomplete.Select2(
url="myapp:result_field_autocomplete",
forward=["field1", "field2", "field3"],
attrs={"data-container-css-class": ""},
),
help_text=constants.RESULT_HELP,
)
Желаемая функциональность:
result_field = Select2ListCreateChoiceField(
widget=autocomplete.Select2(
url="myapp:result_field_autocomplete",
forward=["field1", "field2", "field3"],
attrs={"data-container-css-class": ""},
**free_text=True,**
),
help_text=constants.RESULT_HELP,
)
И что флаг позволит мне ввести ЛЮБОЙ текст по моему выбору.
Я могу получить что-то, изменив виджет на TaggingSelect2
, но это точно не вариант, это неуклюже и у пользователя будет несколько вариантов, что НЕ то, что я хочу.
Идеально все, что мне нужно (если кто-то может придумать другой вариант), это чтобы информация появлялась как начальные данные в форме после того, как я заполню остальные 3 элемента, выполнив эту функцию. Если это можно сделать с помощью другого метода, то это тоже будет замечательно.
Надеюсь, это имеет смысл :)
На случай, если кто-то столкнется с такой же проблемой, мой опыт работы с Javascript очень ограничен, но я пошел на компромисс, сделав кнопку и создав функцию Javascript, которая вызывает представление (так же, как это делает django-autocomplete-light):
Окончательный код:
urls.py:
path(
"result-autocomplete/",
views.MyFunctionAutocomplete,
name="result_autocomplete",
),
views.py:
def MyFunctionAutocomplete(request):
if not request.user.is_authenticated:
return HttpResponse("")
try:
field1 = request.GET["field1"]
field2 = request.GET["field2"]
field3_name = request.GET["field3"]
obj_Field3 = ModelField3.objects.get(name=field3_name)
except AttributeError:
return HttpResponse("")
except ModelField3.DoesNotExist:
return HttpResponse("")
current_result = obj_Field3.get_result(f"{field1}{field2}")
return HttpResponse(current_result["result"])
getresult.js:
function getResult(url){
var field1 = document.querySelector('[id=select2-id_field1-container]').innerText
var field2 = document.querySelector('[id=select2-id_field2-container]').innerText
var field3_name = document.querySelector("#id_field3").selectedOptions[0].innerHTML
if ( field1 && field2 && field3_name){
$.ajax({ // initialize an AJAX request
url: url,
data: {
'field1': field1, // add the field1 to the GET parameters
'field2': field2, // add the field2 to the GET parameters
'field3_name': field3_name // add the field3_name to the GET parameters
},
success: function (data) { // `data` is the return of the `MyFunctionAutocomplete` view function
$("#id_result")[0].value = data ; // replace the contents of the price input with the data that came from the server
$("#id_result")[0].placeholder = data // Not needed really
}
});
}
}
django-template.html:
[...]
<form method="post">
{% csrf_token %}
{% for field in form %}
{% if field.name == "result" %}
<script defer src="{% static 'js/getresult.js' %}"></script>
<button type="button" id="getresult" onclick="getresult('{% url "result_autocomplete"%}')">Get live result</button>
{% bootstrap_field field show_label=True label_class="result" %}
{% else %}
{% bootstrap_field field %}
{% endif %}
{% endfor %}
[...]
Надеюсь, это поможет другим людям вроде меня с не очень хорошими навыками JS и застрявшим с чем-то подобным. Я развернул это решение, и оно работает, но если кто-то видит что-то не так, дайте мне знать :-)