How to send the text in django-select2 field instead of the id at form submission?

521 Views Asked by At

This is a two-part question

Problem description
I have a form with a field that uses ModelSelect2TagWidget (that accepts free-text as an input). when the user enters a number as free-text, it is converted to the object whose id is the number entered as a free-text. For example, if the user enters "11" as free-text
enter image description here
and the form is submitted, the entry is automatically converted to the value whose id is 11 in the database
enter image description here

On the other hand, if the user chooses one of the available choices (not a free-text), the id of the selected choice in the select2 field is sent in the request as in the screenshot.
enter image description here

The problem is when the form is submitted, I have no way to distinguish whether the submitted value is an id or a free-text number entered by the user.

To solve this problem, I am thinking about sending the text of the input field instead of the id as follows
enter image description here

  1. How can I send the text of the selected choice instead of the id in the request?
  2. As this is a ModelSelect2TagWidget field, it accepts free-text. Is there a better way to distinguish between the id of a selected choice or if the user enters a number as a free-text?

Thank you for your help!

1

There are 1 best solutions below

0
On

I used the following solution. Further links AutoResponseView

Subclassed AutoResponseView from django-select2. In response_obj set the "id" value to the field of the model you want to use. In my case was obj.name (instead of obj.pk in AutoResponseView itself)

in a new file select2_classes.py inside my app

class MyResponseView(AutoResponseView):
    def get(self, request, *args, **kwargs):
       self.widget = self.get_widget_or_404()
       self.term = kwargs.get("term", request.GET.get("term", ""))
       self.object_list = self.get_queryset()

       context = self.get_context_data()

       response_obj = {
         "results": [
            {"text": self.widget.label_from_instance(obj), "id": obj.name}
            for obj in context["object_list"]
        ],
        "more": context["page_obj"].has_next(),
       }
       response = JsonResponse(response_obj)
       return response

In urls.py inside app folder

urlpatterns = [
    ...,
    path('my_search/', MyResponseView.as_view(), name='my-search'),
]

In forms.py, added a widget to the corresponding field

from django.urls import reverse_lazy

class MyWidget(ModelSelect2MultipleTagWidget):
    attributes = {'data-minimum-input-length': 0}

    def get_queryset(self):
        return MyModel.objects.all() # or filtered queryset 


class ListSearchForm(forms.Form):
    name = forms.CharField(
          label=...,
          help_text=...,
          widget=MyWidget(data_url=reverse_lazy("app:my-search")))