diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9f37701..f09d291 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,7 +5,7 @@ Changelog for django-agnocomplete master (unreleased) =================== -Nothing here yet. +* Fix bug in AgnocompleteWidgetMixin when template-based widgets are used (Django>=1.11). 0.11.0 (2018-01-25) =================== diff --git a/agnocomplete/widgets.py b/agnocomplete/widgets.py index 3061bb8..f4ef21c 100644 --- a/agnocomplete/widgets.py +++ b/agnocomplete/widgets.py @@ -45,20 +45,6 @@ def _agnocomplete_build_attrs(self, attrs): return attrs - def render_options(self, *args): - # Django >= 1.10, only "selected_choices" in the arg list - if len(args) == 1: - selected_choices = args[0] - else: - # Django < 1.10 - selected_choices is the second arg. - _, selected_choices = args - selected_choices = set(text(v) for v in selected_choices) - selected_choices_tuples = self.agnocomplete.selected(selected_choices) - output = [] - for option_value, option_label in selected_choices_tuples: - output.append(self.render_option(selected_choices, option_value, option_label)) # noqa - return '\n'.join(output) - if StrictVersion(get_version()) < StrictVersion('1.11'): class AgnocompleteWidgetMixin(_AgnocompleteWidgetMixin): @@ -69,6 +55,20 @@ def build_attrs(self, extra_attrs=None, **kwargs): attrs = super(AgnocompleteWidgetMixin, self).build_attrs( extra_attrs, **kwargs) return self._agnocomplete_build_attrs(attrs) + + def render_options(self, *args): + # Django >= 1.10, only "selected_choices" in the arg list + if len(args) == 1: + selected_choices = args[0] + else: + # Django < 1.10 - selected_choices is the second arg. + _, selected_choices = args + selected_choices = set(text(v) for v in selected_choices) + selected_choices_tuples = self.agnocomplete.selected(selected_choices) + output = [] + for option_value, option_label in selected_choices_tuples: + output.append(self.render_option(selected_choices, option_value, option_label)) # noqa + return '\n'.join(output) else: class AgnocompleteWidgetMixin(_AgnocompleteWidgetMixin): """ @@ -79,6 +79,33 @@ def build_attrs(self, base_attrs, extra_attrs=None): base_attrs, extra_attrs) return self._agnocomplete_build_attrs(attrs) + """ + Returns the selected option set in order to retrieve the behaviour of + AgnocompleteWidgetMixin.render_options() + """ + def _agnocomplete_selected_options(self, options, value): + selected_options = {} + + for opt in options: + opt_value = text(opt.get('value')) + opt_selected = (opt_value in value) + + opt['selected'] = opt_selected + opt['attrs']['selected'] = opt_selected + + if opt_selected: + selected_options[opt_value] = opt + + return list(selected_options.values()) + + """ + Render only selected options + """ + def optgroups(self, name, value, attrs=None): + _selected_options = self._agnocomplete_selected_options + for name, options, index in super(AgnocompleteWidgetMixin, self).optgroups(name, value, attrs): + yield (name, _selected_options(options, value), index) + class AgnocompleteSelect(AgnocompleteWidgetMixin, widgets.Select): """ diff --git a/demo/tests/test_fields.py b/demo/tests/test_fields.py index 3e42b96..893db7c 100644 --- a/demo/tests/test_fields.py +++ b/demo/tests/test_fields.py @@ -1,22 +1,34 @@ -from django.test import TestCase -from django.core.urlresolvers import reverse +from distutils.version import StrictVersion +from django import forms, get_version +from django.core.urlresolvers import reverse +from django.test import TestCase import six +from agnocomplete import fields +from agnocomplete.exceptions import UnregisteredAgnocompleteException from agnocomplete.fields import ( AgnocompleteField, AgnocompleteMultipleField, AgnocompleteModelMultipleField, ) -from agnocomplete.exceptions import UnregisteredAgnocompleteException - +from agnocomplete.forms import UserContextFormMixin from demo.autocomplete import ( AutocompleteColor, HiddenAutocompleteURL, HiddenAutocompleteURLReverse, AutocompleteTag, + AutocompletePersonDomain, ) -from demo.models import Tag +from demo.models import Tag, Person +from demo.tests import LoaddataTestCase + + +def is_selected_attr(): + if StrictVersion(get_version()) < StrictVersion('1.11'): + return 'selected="selected"' + else: + return 'selected' class AgnocompleteInstanceTest(TestCase): @@ -70,8 +82,57 @@ def test_class_url_reversed(self): ) -class MultipleSelectTest(TestCase): +class ModelSelectTest(LoaddataTestCase): + class _Form(UserContextFormMixin, forms.Form): + person = fields.AgnocompleteModelField(AutocompletePersonDomain, + to_field_name='email') + + def setUp(self): + super(ModelSelectTest, self).setUp() + self.alice = Person.objects.get(pk=1) + self.bob = Person.objects.get(pk=3) + + self.invalid_pk = Person.objects.order_by('pk').last().pk + 1 + + def test_render(self): + form = self._Form( + user=None, + data={'person': self.bob.email}, + ) + + # bob is selected => only bob