Свободный текст в 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 и застрявшим с чем-то подобным. Я развернул это решение, и оно работает, но если кто-то видит что-то не так, дайте мне знать :-)

Вернуться на верх