Skip to content

Commit c15de46

Browse files
mfrascacodingjoe
authored andcommitted
Fix applegrew#544 -- Ensure correct attribute defaults (applegrew#547)
dict.setdefault() does not change the default value if called twice. Therefore, defaults need to passed to the super call instead.
1 parent 898b2e8 commit c15de46

File tree

4 files changed

+83
-26
lines changed

4 files changed

+83
-26
lines changed

django_select2/forms.py

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -70,16 +70,18 @@ class Select2Mixin:
7070
form media.
7171
"""
7272

73-
def build_attrs(self, *args, **kwargs):
73+
def build_attrs(self, base_attrs, extra_attrs=None):
7474
"""Add select2 data attributes."""
75-
attrs = super(Select2Mixin, self).build_attrs(*args, **kwargs)
75+
default_attrs = {'data-minimum-input-length': 0}
7676
if self.is_required:
77-
attrs.setdefault('data-allow-clear', 'false')
77+
default_attrs['data-allow-clear'] = 'false'
7878
else:
79-
attrs.setdefault('data-allow-clear', 'true')
80-
attrs.setdefault('data-placeholder', '')
79+
default_attrs['data-allow-clear'] = 'true'
80+
default_attrs['data-placeholder'] = ''
81+
82+
default_attrs.update(base_attrs)
83+
attrs = super().build_attrs(default_attrs, extra_attrs=extra_attrs)
8184

82-
attrs.setdefault('data-minimum-input-length', 0)
8385
if 'class' in attrs:
8486
attrs['class'] += ' django-select2'
8587
else:
@@ -120,12 +122,15 @@ def _get_media(self):
120122
class Select2TagMixin:
121123
"""Mixin to add select2 tag functionality."""
122124

123-
def build_attrs(self, *args, **kwargs):
125+
def build_attrs(self, base_attrs, extra_attrs=None):
124126
"""Add select2's tag attributes."""
125-
self.attrs.setdefault('data-minimum-input-length', 1)
126-
self.attrs.setdefault('data-tags', 'true')
127-
self.attrs.setdefault('data-token-separators', '[",", " "]')
128-
return super(Select2TagMixin, self).build_attrs(*args, **kwargs)
127+
default_attrs = {
128+
'data-minimum-input-length': 1,
129+
'data-tags': 'true',
130+
'data-token-separators': '[",", " "]'
131+
}
132+
default_attrs.update(base_attrs)
133+
return super().build_attrs(default_attrs, extra_attrs=extra_attrs)
129134

130135

131136
class Select2Widget(Select2Mixin, forms.Select):
@@ -226,20 +231,27 @@ def get_url(http://github.com/self):
226231
return self.data_url
227232
return reverse(self.data_view)
228233

229-
def build_attrs(self, *args, **kwargs):
234+
def build_attrs(self, base_attrs, extra_attrs=None):
230235
"""Set select2's AJAX attributes."""
231-
attrs = super(HeavySelect2Mixin, self).build_attrs(*args, **kwargs)
236+
237+
default_attrs = {
238+
'data-ajax--url': self.get_url(),
239+
'data-ajax--cache': "true",
240+
'data-ajax--type': "GET",
241+
'data-minimum-input-length': 2,
242+
}
243+
244+
if self.dependent_fields:
245+
default_attrs['data-select2-dependent-fields'] = " ".join(self.dependent_fields)
246+
247+
default_attrs.update(base_attrs)
248+
249+
attrs = super().build_attrs(default_attrs, extra_attrs=extra_attrs)
232250

233251
# encrypt instance Id
234252
self.widget_id = signing.dumps(id(self))
235253

236254
attrs['data-field_id'] = self.widget_id
237-
attrs.setdefault('data-ajax--url', self.get_url())
238-
attrs.setdefault('data-ajax--cache', "true")
239-
attrs.setdefault('data-ajax--type', "GET")
240-
attrs.setdefault('data-minimum-input-length', 2)
241-
if self.dependent_fields:
242-
attrs.setdefault('data-select2-dependent-fields', " ".join(self.dependent_fields))
243255

244256
attrs['class'] += ' django-select2-heavy'
245257
return attrs

tests/conftest.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import string
33

44
import pytest
5-
from django.utils.encoding import force_text
65
from selenium import webdriver
76
from selenium.common.exceptions import WebDriverException
87

@@ -22,11 +21,11 @@ def random_name(n):
2221
@pytest.yield_fixture(scope='session')
2322
def driver():
2423
chrome_options = webdriver.ChromeOptions()
25-
chrome_options.headless = True
24+
chrome_options.headless = False
2625
try:
2726
b = webdriver.Chrome(options=chrome_options)
2827
except WebDriverException as e:
29-
pytest.skip(force_text(e))
28+
pytest.skip(str(e))
3029
else:
3130
yield b
3231
b.quit()

tests/test_forms.py

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,12 +217,17 @@ def test_multiple_widgets(self, db, live_server, driver):
217217
driver.find_element_by_css_selector('.select2-results')
218218

219219
elem1, elem2 = driver.find_elements_by_css_selector('.select2-selection')
220-
elem1.click()
221220

221+
elem1.click()
222+
search1 = driver.find_element_by_css_selector('.select2-search__field')
223+
search1.send_keys('fo')
222224
result1 = WebDriverWait(driver, 60).until(
223225
expected_conditions.presence_of_element_located((By.CSS_SELECTOR, '.select2-results li:first-child'))
224226
).text
227+
225228
elem2.click()
229+
search2 = driver.find_element_by_css_selector('.select2-search__field')
230+
search2.send_keys('fo')
226231
result2 = WebDriverWait(driver, 60).until(
227232
expected_conditions.presence_of_element_located((By.CSS_SELECTOR, '.select2-results li:first-child'))
228233
).text
@@ -315,6 +320,37 @@ def test_get_queryset(self):
315320
widget.queryset = Genre.objects.all()
316321
assert isinstance(widget.get_queryset(), QuerySet)
317322

323+
def test_tag_attrs_Select2Widget(self):
324+
widget = Select2Widget()
325+
output = widget.render('name', 'value')
326+
assert 'data-minimum-input-length="0"' in output
327+
328+
def test_custom_tag_attrs_Select2Widget(self):
329+
widget = Select2Widget(attrs={'data-minimum-input-length': '3'})
330+
output = widget.render('name', 'value')
331+
assert 'data-minimum-input-length="3"' in output
332+
333+
def test_tag_attrs_ModelSelect2Widget(self):
334+
widget = ModelSelect2Widget(queryset=Genre.objects.all(), search_fields=['title__icontains'])
335+
output = widget.render('name', 'value')
336+
assert 'data-minimum-input-length="2"' in output
337+
338+
def test_tag_attrs_ModelSelect2TagWidget(self):
339+
widget = ModelSelect2TagWidget(queryset=Genre.objects.all(), search_fields=['title__icontains'])
340+
output = widget.render('name', 'value')
341+
assert 'data-minimum-input-length="2"' in output
342+
343+
def test_tag_attrs_HeavySelect2Widget(self):
344+
widget = HeavySelect2Widget(data_url='/foo/bar/')
345+
output = widget.render('name', 'value')
346+
assert 'data-minimum-input-length="2"' in output
347+
348+
def test_custom_tag_attrs_ModelSelect2Widget(self):
349+
widget = ModelSelect2Widget(
350+
queryset=Genre.objects.all(), search_fields=['title__icontains'], attrs={'data-minimum-input-length': '3'})
351+
output = widget.render('name', 'value')
352+
assert 'data-minimum-input-length="3"' in output
353+
318354
def test_get_search_fields(self):
319355
widget = ModelSelect2Widget()
320356
with pytest.raises(NotImplementedError):
@@ -382,7 +418,7 @@ class TestHeavySelect2TagWidget(TestHeavySelect2Mixin):
382418
def test_tag_attrs(self):
383419
widget = ModelSelect2TagWidget(queryset=Genre.objects.all(), search_fields=['title__icontains'])
384420
output = widget.render('name', 'value')
385-
assert 'data-minimum-input-length="1"' in output
421+
assert 'data-minimum-input-length="2"' in output
386422
assert 'data-tags="true"' in output
387423
assert 'data-token-separators' in output
388424

tests/testapp/forms.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,11 +149,19 @@ class HeavySelect2WidgetForm(forms.Form):
149149
class HeavySelect2MultipleWidgetForm(forms.Form):
150150
title = forms.CharField(max_length=50)
151151
genres = forms.MultipleChoiceField(
152-
widget=HeavySelect2MultipleWidget(data_view='heavy_data_1', choices=NUMBER_CHOICES),
152+
widget=HeavySelect2MultipleWidget(
153+
data_view='heavy_data_1',
154+
choices=NUMBER_CHOICES,
155+
attrs={'data-minimum-input-length': 0},
156+
),
153157
choices=NUMBER_CHOICES
154158
)
155159
featured_artists = forms.MultipleChoiceField(
156-
widget=HeavySelect2MultipleWidget(data_view='heavy_data_2', choices=NUMBER_CHOICES),
160+
widget=HeavySelect2MultipleWidget(
161+
data_view='heavy_data_2',
162+
choices=NUMBER_CHOICES,
163+
attrs={'data-minimum-input-length': 0},
164+
),
157165
choices=NUMBER_CHOICES,
158166
required=False
159167
)
@@ -182,6 +190,7 @@ class AddressChainedSelect2WidgetForm(forms.Form):
182190
search_fields=['name__icontains'],
183191
max_results=500,
184192
dependent_fields={'city': 'cities'},
193+
attrs={'data-minimum-input-length': 0},
185194
)
186195
)
187196

@@ -193,6 +202,7 @@ class AddressChainedSelect2WidgetForm(forms.Form):
193202
search_fields=['name__icontains'],
194203
dependent_fields={'country': 'country'},
195204
max_results=500,
205+
attrs={'data-minimum-input-length': 0},
196206
)
197207
)
198208

0 commit comments

Comments
 (0)