Problem with autocomplete with Django Rest API

In my Django project, I have a Search field. I used Select2 autocomplete with it. I needed to fetch the product_list from my Product model. So I created a rest API that returns the product in json formats.

Here is my rest API code:

serializer.py:

class ProductSerializer(serializers.ModelSerializer):
    class Meta:
        model = ProductList
        fields = ('product_id', 'product_name', 'product_image', 'product_available',
                  'product_description')

views.py:

class JSONResponse(HttpResponse):
    def __init__(self, data, **kwargs):
        content = JSONRenderer().render(data)
        kwargs['content_type'] = 'application/json'
        super(JSONResponse, self).__init__(content, **kwargs)


def list(request):
    if request.method == 'GET':
        products = ProductList.objects.filter(product_name__icontains=request.GET.get('q'))
        serializer = ProductSerializer(products, many=True)
        serializer_data = serializer.data
        customData = {'results': serializer_data}
        return JSONResponse(customData)

Now in my html, in the javascript portion I used this code mentioned in this Select2 doc. The code I used, looks like this:

base.html:

<script type="text/javascript">
        $(document).ready(function() {
            $('.js-data-example-ajax').select2({
                ajax: {
                    url: "/api.alif-marine.com/search/products",
                    dataType: 'json',
                    delay: 250,
                    type: 'GET',
                    data: function (params) {
                        return{
                            q: params.term, // search term
                            page: params.page
                        };
                    },
                    processResults: function (data, params) {
                        params.page = params.page || 1;

                        return {
                            results: data.results,
                        };
                    },
                    cache: true
                },
                placeholder: 'Search for a product',
                escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
                minimumInputLength: 1,
                templateResult: formatRepo,
                templateSelection: formatRepoSelection
            });
            function formatRepo (repo) {
                if (repo.loading) {
                    return repo.text;
                }

                var markup = "<div class='select2-result-repository clearfix'>" +
{#                    "<div class='select2-result-repository__avatar'><img src='" + repo.owner.avatar_url + "' /></div>" +#}
                    "<div class='select2-result-repository__meta'>" +
                    "<div class='select2-result-repository__title'>" + repo.product_name + "</div>";

                if (repo.product_description) {
                    markup += "<div class='select2-result-repository__description'>" + repo.product_description + "</div>";
                }

                return markup;
            }

            function formatRepoSelection (repo) {
                return repo.product_name || repo.text;
            }
        });
    </script>

When I used Postman to check if the rest API works or not, it worked perfectly. For my query in the Postman like these:

localhost:8000/api.alif-marine.com/search/products?q=t

or

localhost:8000/api.alif-marine.com/search/products?q=tho

or

localhost:8000/api.alif-marine.com/search/products?q=thomas

The retrieved json data is given below for query localhost:8000/api.alif-marine.com/search/products?q=t :

{  
   "results":[  
      {  
         "product_id":9,
         "product_name":"thomas",
         "product_image":"/media/media/tom_dushtu.jpg",
         "product_available":"available",
         "product_description":"jah dushtu"
      },
      {  
         "product_id":8,
         "product_name":"ami dissapointed",
         "product_image":"/media/media/dissapointment.jpg",
         "product_available":"available",
         "product_description":"I ma kinda dissapointed, you know.................."
      }
   ]
}

Now with all those, I couldn’t make it work. The autocomplete is not working. Nothing is shown when I press one key or write the name of the whole product.

Here is an image. bal

It always has shown Searching… I tried reading the issues on the Github repo and some other things but couldn’t solve it.

What am I doing wrong?

Select2 requires both an id and a text attribute for each result. See https://select2.org/data-sources/formats. So, you need to either return those in your API response, or use processResults to generate them client-side after the response is received.

Also, it looks like you copy-pasted the templateResult callback from the demo. Any reason? You should name your callbacks and craft your code to reflect the needs of your specific application, rather than blindly copy-pasting from examples. This is how you make your code unreadable and debugging more difficult later on.

Please, don’t be a copy-paste programmer if you want to be taken seriously in this profession.

Thanks for your kind reply and suggestions. I will try to follow your advice.

Actually the thing is, I tried my own <script> .... </script> following your tutorial from here. But it didn’t work. So I just thought, "as the example was working so let’s use the template and see what happens? "

I shouldn’t have posted that here. I should have posted the codes which I was trying. It’s not good what I did. And I am apologizing for my mistake, Sir.

My actual code is this:

<script type="text/javascript">
    $(document).ready(function() {
        $('.js-data-example-ajax').select2({
            ajax: {
                url: "http://localhost:8000/api.alif-marine.com/search/products/",
					dataType: 'json',
					delay: 250,
                data: function (params) {
                    return{
							q:params
						};
                },
                transport: function (params, success, failure) {
                    var $request = $.ajax(params);
                    $request.then('success', success);
                    $request.fail('failure', failure);
                    return $request;
                },
                processResults: function (data) {
                    return{
                        results: data
                    };
                },
                cache: true
            },
            placeholder: 'Search for a product',
            escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
            minimumInputLength: 1,
            templateSelection: formatSelection
        });
        function formatSelection (results) {
            return '<option value="'+ results.id +'">' + results.text + '</option>'
        }
    });
</script>

I should also mention that I updated my API response. Now it returns response just like you mentioned in your Doc.

{
  "results": [
    {
      "id": 1,
      "text": "Option 1"
    },
    ]
}

And instead using this:

transport: function (params, success, failure) {
    var $request = $.ajax(params);
    $request.then('success', success);
    $request.fail('failure', failure);
    return $request;
},

I used this at first :

transport: function (params, success, failure) {
      var request = new AjaxRequest(params.url, params);
      request.on('success', success);
      request.on('failure', failure);
}, 

But then I got the following error:
select1
And then I switched back to this:

transport: function (params, success, failure) {
    var $request = $.ajax(params);
    $request.then('success', success);
    $request.fail('failure', failure);
    return $request;
},

And now I get this error:
select2

Now I am really hopeless. I really don’t understand what is going on or what am I doing wrong?

Sir, if you kindly help me it would mean a lot to me. As I have never used any autocomplete before and your library looks to me well documented than others and I liked it very much.

I am just an undergrad student and I am using this for my term project. I am learning this, Sir. So I might have made some mistakes. Please pardon me.

No problem, as long as you keep learning :slight_smile:

You don’t need to define a custom transport callback for ajax requests to work. Try removing that completely - I’m not really sure why it’s in the example in the first place. Perhaps we should remove it.

First,
I removed it completely. But still, I am getting errors.

I created my own Django API, which returns the product id and product name in the given format, you specified in your Doc (id, text). And the API sends the response as JSON response. I set the url for my API like this:

url('^api.alif-marine.com/search/products/', autocomplete_views.list, name='productsAPI'),

And in the functions, from where I am returning the results, looks like this (mentioned earlier):

def list(request):
if request.method == ‘GET’:
print(request.GET.get(‘q’))
products = ProductList.objects.filter(product_name__icontains=request.GET.get(‘q’))
serializer = ProductSerializer(products, many=True)
serializer_data = serializer.data
customdata = {‘results’: serializer_data}
return JSONResponse(customdata)

Here you can see, I am looking for an url

http://localhost:8000/api.alif-marine.com/search/products/?q=what_user_types

Now, I actually I don’t know if it’s right, I mean if the request is going to the expected url or it is Json response. As I have found that there are some issues where working with Json in Select2 make some problems. I found it in this select2 google groups

Second,

After I completely removed the transport tag, the error I am getting is this:
select3

From the errors I think that, it might be problems with handleSearch shown there. I actually don’t know what it does. I really didn’t go through the select2.js as I know I won’t understand a thing. It also looks like that there might be some problems with d in d.query, d.trigger, d.invoke as mentioned in the errors.

Third,
I found from this StackOverflow question’s answer that in Select2 version 4 there are some change occurs. It says there:

(2) Change the processResults function so the results property of the object it returns is an array of objects, where each object has an id and a text property. One way to do this is to use the $.map() function to create a new array from the one that is returned by the ajax call.

processResults: function (data) {
return {
results: $.map(data, function(obj) {
return { id: obj.ime, text: obj.ime };
})
};
}

So I used it in my processResults and instead of return { id: obj.ime, text: obj.ime } this I used return { id: obj.id, text: obj.text } this.

But no luck. I actually don’t know what should I do now.

If your API is returning the data in the correct format, then we really don’t need to care about your server-side code at this point.

It sounds like you’re just throwing every single AJAX option into your code, without really paying attention to whether or not you need it. Start with the simplest example at the beginning of https://select2.org/data-sources/ajax. Then read through, and decide which additional options you need.

The data param is a callback that lets you map the current search term, to a query string parameter in your request. What query string parameters does your API accept?

The processResults callback transforms your API data into a form that can be used by Select2. If your API is already returning the data in Select2’s expected format, you don’t need to set this.

From your documentation, from here, is that in the data part
data: function(params){ return{ q:params.term } }
what I understood is, it creates something like this ?q=the_word_I_type
Therefore it’s gonna get added up on the url. That’s how I designed my API.

In order to get the data from my API, the whole url is
http://localhost:8000/api.alif-marine.com/search/products/?q=the_word_user_type

And in my views.py I was querying the database with this code:

products = ProductList.objects.filter(product_name__icontains=request.GET.get('q'))

Here I was searching for q in my url with this pattern : ?q=blah_blah

So, this is the string parameters my API accept.

Yep! Sounds like you’ve got that part correct, then.

BTW, posting error messages from the minified version of Select2 is completely useless. Not sure why people keep doing this.

I looked for this issues on Google and many people said that it should be looked up the on the Console and Network section of the Inspect Element from my browser.

So that’s the part where it gives me the minified version errors.

And I didn’t use any css or js codes from Select2 that stays on my machine.
I was using the following

js
<script src="https://code.jquery.com/jquery-3.1.1.slim.min.js" integrity="sha384-A7FZj7v+d/sdmMqp/nOQwliLvUsJfDHW+k9Omg/a/EheAdgtzNs3hpfag6Ed950n" crossorigin="anonymous"></script>

after this jquery the select2

<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/js/select2.min.js"></script>

css

<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/css/select2.min.css" rel="stylesheet" />

So that’s the errors I have now.

Your use of the browser console is not the problem. Switch to using the unminified versions of the .js and .css .

Okay. So I have downloaded the codes. Now the error I get is

select2.min.js:1 Uncaught TypeError: c.ajax is not a function
    at Object.transport (select2.min.js:1)
    at d (select2.min.js:1)

I looked at the minified version and it gives me this:

transport:function(a,b,d){var e=c.ajax(a);return e.then(b),e.fail(d),e}};

Then I looked up in the downloaded select2.js in the transport section and there’s a transport section of course.

transport: function (params, success, failure) {
        var $request = $.ajax(params);

        $request.then(success);
        $request.fail(failure);

        return $request;
      }

Now if I mapped what I got from the errors to the js codes

var e=c.ajax(a) equivalent var $request = $.ajax(params);

So the error c.ajax is not a function has something to do with $.ajax(params);

:man_facepalming:

You’re loading the “slim” version of jQuery, which apparently doesn’t include $.ajax.

This is why it is important to understand the code that you’re copy-pasting!

How on earth am I supposed to know that? :frowning_face: I don’t think there’s anything in the documentation!!!

Anyway, what should I change/do?

Use the full version of jQuery, instead of the slim version.

I don’t understand it quite properly.

Did you mean instead of using this:
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/js/select2.min.js"></script>

I should use the select2.js which I downloaded manually?

So I changed the slim version to normal js
and it is this:
<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256 hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>

And now I get this error :frowning:
help

Now if you look at the GET there are somethings in the url after products/?q. I didn’t write those and according to Select2 the url should be

http://localhost:8000/api.alif-marine.com/search/products/?q=thomas.

And also I don’t understand the errors. What might cause the error??

You’re getting a CORS error because the domain of your page (127.0.0.1) does not match the domain of the URL you are making the request to (localhost). So, you need to fix this so that the domains are exactly the same.

This question is starting to go beyond the scope of Select2. Can you spend some time with a book or Youtube tutorials to learn some of these basic web application and HTTP concepts?

Thanks for your kind suggestions.

I solved it. Thanks a lot lot, Sir.

Without your help, I would probably wind up hard-coded it.

Phew, probably gonna save my grade this term

Thanks again.

1 Like