diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py
index 9464107..27c4095 100644
--- a/django/contrib/admin/widgets.py
+++ b/django/contrib/admin/widgets.py
@@ -7,6 +7,7 @@ from django.utils.datastructures import MultiValueDict
 from django.utils.text import capfirst, truncate_words
 from django.utils.translation import ugettext as _
 from django.utils.safestring import mark_safe
+from django.utils.encoding import force_unicode
 from django.conf import settings
 
 class FilteredSelectMultiple(forms.SelectMultiple):
@@ -135,6 +136,16 @@ class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
         if value:
             return [value]
         return None
+    
+    def _has_changed(self, initial, data):
+        if data is None:
+            data = []
+        if len(initial) != len(data):
+            return True
+        for pk1, pk2 in zip(initial, data):
+            if force_unicode(pk1) != force_unicode(pk2):
+                return True
+        return False
 
 class RelatedFieldWidgetWrapper(object):
     """
diff --git a/django/newforms/widgets.py b/django/newforms/widgets.py
index 16e6173..3e70972 100644
--- a/django/newforms/widgets.py
+++ b/django/newforms/widgets.py
@@ -401,6 +401,16 @@ class SelectMultiple(Widget):
         if isinstance(data, MultiValueDict):
             return data.getlist(name)
         return data.get(name, None)
+    
+    def _has_changed(self, initial, data):
+        if data is None:
+            data = []
+        if len(initial) != len(data):
+            return True
+        for pk1, pk2 in zip(initial, data):
+            if force_unicode(pk1) != force_unicode(pk2):
+                return True
+        return False
 
 class RadioInput(StrAndUnicode):
     """
diff --git a/tests/regressiontests/admin_widgets/models.py b/tests/regressiontests/admin_widgets/models.py
index 7207e8e..e32a200 100644
--- a/tests/regressiontests/admin_widgets/models.py
+++ b/tests/regressiontests/admin_widgets/models.py
@@ -66,6 +66,14 @@ Currently: <a target="_blank" href="%(MEDIA_URL)stest">test</a> <br />Change: <i
 >>> w = ManyToManyRawIdWidget(rel)
 >>> print conditional_escape(w.render('test', [m1.pk, m2.pk], attrs={}))
 <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>
+>>> w._has_changed([], None)
+False
+>>> w._has_changed([1, 2], [u'1', u'2'])
+False
+>>> w._has_changed([1, 2], [u'1'])
+True
+>>> w._has_changed([1, 2], [u'1', u'3'])
+True
 
 """ % {
     '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/tests/regressiontests/forms/widgets.py
+++ b/tests/regressiontests/forms/widgets.py
@@ -612,6 +612,16 @@ If 'choices' is passed to both the constructor and render(), then they'll both b
 >>> w.render('nums', ['ŠĐĆŽćžšđ'], choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')])
 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>'
 
+# Test the usage of _has_changed
+>>> w._has_changed([], None)
+False
+>>> w._has_changed([1, 2], [u'1', u'2'])
+False
+>>> w._has_changed([1, 2], [u'1'])
+True
+>>> w._has_changed([1, 2], [u'1', u'3'])
+True
+
 # RadioSelect Widget ##########################################################
 
 >>> w = RadioSelect()
