applegrew / django-select2

This is a Django integration for Select2

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Model widgets do not follow data-minimum-input-length

mfrasca opened this issue · comments

Describe the bug
Model widgets don't seem to set nor use the data-minimum-input-length value. data is requested via ajax even on empty input.

Code Snippet

class AccessionForm(forms.ModelForm):
    class Meta:
        model = Accession
        fields = ['code', 'received_quantity', 'received_type', 'source', 'accessioned_date', 'received_date', ]
        widgets = {
            'source': s2forms.ModelSelect2Widget(model=Contact,
                                                 search_fields=['name__icontains']),
            'received_type': s2forms.Select2Widget,
        }

produces (edited for easier reading)

<select name="source" data-ajax--cache="true"
   class="django-select2 django-select2-heavy select2-hidden-accessible"
   data-ajax--type="GET" data-minimum-input-length="0" data-ajax--url="/select2/fields/auto.json"
   data-field_id="MTQwMjI4MDg1OTcxODgw:1hNiFv:rQwuma5lz_zSWcYWZ--FJsNPGu0"
   data-allow-clear="true" id="id_source" data-placeholder="" tabindex="-1" aria-hidden="true"
   style="width: 100%;">
  <option value=""></option>
</select>

which is not what I expect, since this is a "heavy" widget, and it should get a "2" minimum length value. so I edit with the code

             $('select.django-select2-heavy').attr('data-minimum-input-length', '2')

and still the query is executed even on empty input, or less than length 1:

[06/May/2019 18:24:51] "GET /select2/fields/auto.json?field_id=MTQwMjI4MDg1OTcxODgw%3A1hNiFv%3ArQwuma5lz_zSWcYWZ--FJsNPGu0 HTTP/1.1" 200 271
[06/May/2019 18:24:52] "GET /select2/fields/auto.json?term=m&field_id=MTQwMjI4MDg1OTcxODgw%3A1hNiFv%3ArQwuma5lz_zSWcYWZ--FJsNPGu0 HTTP/1.1" 200 152

Hi @mfrasca good catch, that is a bug in

attrs.setdefault('data-minimum-input-length', 2)

If you use setdefault twice, it does not overwrite.

>>> a = {}
>>> a.setdefault('foo', 'bar')
'bar'
>>> a.setdefault('foo', 'baz')
'bar'
>>> a
{'foo': 'bar'}

Since you found the bug, would you care to fix it and submit a PR?

so your code is applying defaults while collecting them. I'll have a look.

so your code is applying defaults while collecting them. I'll have a look.

Yes, correct. Excellent catch, everyone seems to have ignored that issue before.

is this a correct test?

@@ -315,6 +315,17 @@ class TestModelSelect2Mixin(TestHeavySelect2Mixin):
         widget.queryset = Genre.objects.all()
         assert isinstance(widget.get_queryset(), QuerySet)
 
+    def test_tag_attrs(self):
+        widget = ModelSelect2Widget(queryset=Genre.objects.all(), search_fields=['title__icontains'])
+        output = widget.render('name', 'value')
+        assert 'data-minimum-input-length="2"' in output
+
+    def test_custom_tag_attrs(self):
+        widget = ModelSelect2Widget(
+            queryset=Genre.objects.all(), search_fields=['title__icontains'], attrs={'data-minimum-input-length': '3'})
+        output = widget.render('name', 'value')
+        assert 'data-minimum-input-length="3"' in output
+
     def test_get_search_fields(self):
         widget = ModelSelect2Widget()
         with pytest.raises(NotImplementedError):

@mfrasca yes that does look correct, however I would add tests for all sub classes of Select2Mixin, Select2TagMixin and HeavySelect2Mixin, since they all are supposed to have different defaults for data-minimum-input-length.

I'll have a look then. I will create a new pull request containing the additional tests, then we can work on polishing them, making them succeed, so you can merge the PR.

(just a useless remark about "everyone seems to have ignored", my experience at office, while I was still working in a office, is that most of my colleagues fetched free software, judged it as if it were a black box, and behaved as if shopping, or if they do find a bug and fix it, they mostly did not consider providing feedback.) (Giorgio Gaber in his song "La Libertà": "… la libertà è partecipazione.")

why does this one look different than the others?

    def build_attrs(self, *args, **kwargs):
        """Add select2's tag attributes."""
        print('data-minimum-input-length was {} when setting to 1'.format(self.attrs.get('data-minimum-input-length')))
        self.attrs.setdefault('data-minimum-input-length', 1)
        self.attrs.setdefault('data-tags', 'true')
        self.attrs.setdefault('data-token-separators', '[",", " "]')
        return super(Select2TagMixin, self).build_attrs(*args, **kwargs)

you're namely writing into self.attrs, while the other build_attrs work on a 'floating' variable, and I've not yet seen where you use that and write into self.attrs