diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py
index 9464107..27c4095 100644
a
|
b
|
from django.utils.datastructures import MultiValueDict
|
7 | 7 | from django.utils.text import capfirst, truncate_words |
8 | 8 | from django.utils.translation import ugettext as _ |
9 | 9 | from django.utils.safestring import mark_safe |
| 10 | from django.utils.encoding import force_unicode |
10 | 11 | from django.conf import settings |
11 | 12 | |
12 | 13 | class FilteredSelectMultiple(forms.SelectMultiple): |
… |
… |
class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
|
135 | 136 | if value: |
136 | 137 | return [value] |
137 | 138 | return None |
| 139 | |
| 140 | def _has_changed(self, initial, data): |
| 141 | if data is None: |
| 142 | data = [] |
| 143 | if len(initial) != len(data): |
| 144 | return True |
| 145 | for pk1, pk2 in zip(initial, data): |
| 146 | if force_unicode(pk1) != force_unicode(pk2): |
| 147 | return True |
| 148 | return False |
138 | 149 | |
139 | 150 | class RelatedFieldWidgetWrapper(object): |
140 | 151 | """ |
diff --git a/django/newforms/widgets.py b/django/newforms/widgets.py
index 16e6173..3e70972 100644
a
|
b
|
class SelectMultiple(Widget):
|
401 | 401 | if isinstance(data, MultiValueDict): |
402 | 402 | return data.getlist(name) |
403 | 403 | return data.get(name, None) |
| 404 | |
| 405 | def _has_changed(self, initial, data): |
| 406 | if data is None: |
| 407 | data = [] |
| 408 | if len(initial) != len(data): |
| 409 | return True |
| 410 | for pk1, pk2 in zip(initial, data): |
| 411 | if force_unicode(pk1) != force_unicode(pk2): |
| 412 | return True |
| 413 | return False |
404 | 414 | |
405 | 415 | class RadioInput(StrAndUnicode): |
406 | 416 | """ |
diff --git a/tests/regressiontests/admin_widgets/models.py b/tests/regressiontests/admin_widgets/models.py
index 7207e8e..e32a200 100644
a
|
b
|
Currently: <a target="_blank" href="%(MEDIA_URL)stest">test</a> <br />Change: <i
|
66 | 66 | >>> w = ManyToManyRawIdWidget(rel) |
67 | 67 | >>> print conditional_escape(w.render('test', [m1.pk, m2.pk], attrs={})) |
68 | 68 | <input type="text" name="test" value="1,2" class="vManyToManyRawIdAdminField" /><a href="../../../admin_widgets/member/" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup"></a> |
| 69 | >>> w._has_changed([], None) |
| 70 | False |
| 71 | >>> w._has_changed([1, 2], [u'1', u'2']) |
| 72 | False |
| 73 | >>> w._has_changed([1, 2], [u'1']) |
| 74 | True |
| 75 | >>> w._has_changed([1, 2], [u'1', u'3']) |
| 76 | True |
69 | 77 | |
70 | 78 | """ % { |
71 | 79 | 'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX, |
diff --git a/tests/regressiontests/forms/widgets.py b/tests/regressiontests/forms/widgets.py
index 4944a26..25e0079 100644
a
|
b
|
If 'choices' is passed to both the constructor and render(), then they'll both b
|
612 | 612 | >>> w.render('nums', ['ŠĐĆŽćžšđ'], choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')]) |
613 | 613 | u'<select multiple="multiple" name="nums">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" selected="selected">\u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</option>\n<option value="\u0107\u017e\u0161\u0111">abc\u0107\u017e\u0161\u0111</option>\n</select>' |
614 | 614 | |
| 615 | # Test the usage of _has_changed |
| 616 | >>> w._has_changed([], None) |
| 617 | False |
| 618 | >>> w._has_changed([1, 2], [u'1', u'2']) |
| 619 | False |
| 620 | >>> w._has_changed([1, 2], [u'1']) |
| 621 | True |
| 622 | >>> w._has_changed([1, 2], [u'1', u'3']) |
| 623 | True |
| 624 | |
615 | 625 | # RadioSelect Widget ########################################################## |
616 | 626 | |
617 | 627 | >>> w = RadioSelect() |