Commits

Ian Struble committed fb6fad2

Made dismissAddAnotherPopup autocomplete aware.

Comments (0)

Files changed (5)

django/contrib/admin/media/js/admin/RelatedObjectLookups.js

             elem.options[elem.options.length] = o;
             o.selected = true;
         } else if (elem.nodeName == 'INPUT') {
-            if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) {
+            var autocomplete_elem = django && django.jQuery && django.jQuery(elem);
+            if (!!autocomplete_elem && !!autocomplete_elem.data('djangoautocomplete')) {
+                django.jQuery.getJSON(
+                    autocomplete_elem.data('djangoautocomplete').options.source,
+                    {term: newId, by_id: 1},
+                    function (data) {
+                        // Pass the returned item to the normal
+                        // autocomplete onSelect handler.
+                        autocomplete_elem.data('autocomplete')
+                            .options.select({}, {item: data[0]});
+                    });
+            } else if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) {
                 elem.value += ',' + newId;
             } else {
                 elem.value = newId;

django/contrib/admin/options.py

 
     def autocomplete_view(self, request, field, extra_content=None):
         query = request.GET.get('term', None)
+        query_by_id = request.GET.get('by_id', None) is not None
         
         if field not in self.autocomplete_fields or query is None:
             raise Http404
             else:
                 return "%s__icontains" % field_name
         
+        if query_by_id:
+            # lookup only via an exact match on id
+            settings['fields'] = ('=id',)
+
         for bit in query.split():
             or_queries = [models.Q(**{construct_search(
                 smart_str(field_name)): bit})

django/contrib/admin/widgets.py

         hidden_id = 'id_hidden_%s' % name
         hidden_attrs = self.build_attrs(type='hidden', name=name, value=value, id=hidden_id)
         normal_attrs = self.build_attrs(attrs, type='text')
-        if value:
-            normal_attrs['value'] = self.label_for_value(value)
+        normal_attrs['value'] = self.label_for_value(value)
         if not self.js_options.get('source'):
             self.js_options['source'] = self.get_autocomplete_url(name)
         options = simplejson.dumps(self.js_options)

tests/regressiontests/admin_widgets/tests.py

 from django.contrib.admin import widgets
 from django.contrib.admin.widgets import FilteredSelectMultiple, AdminSplitDateTime
 from django.contrib.admin.widgets import (AdminFileWidget, ForeignKeyRawIdWidget,
-    ManyToManyRawIdWidget)
+    ManyToManyRawIdWidget,
+    AutocompleteWidget, MultipleAutocompleteWidget)
 from django.core.files.storage import default_storage
 from django.core.files.uploadedfile import SimpleUploadedFile
 from django.db.models import DateField
 from django.test import TestCase as DjangoTestCase
+from django.utils import simplejson
 from django.utils.html import conditional_escape
 from django.utils.translation import activate, deactivate
 from django.utils.unittest import TestCase
         self.assertEqual(w._has_changed([1, 2], [u'1', u'2']), False)
         self.assertEqual(w._has_changed([1, 2], [u'1']), True)
         self.assertEqual(w._has_changed([1, 2], [u'1', u'3']), True)
+
+
+class AutocompleteWidgetTest(DjangoTestCase):
+    def test_render(self):
+        band = models.Band.objects.create(name='Linkin Park')
+        band.album_set.create(
+            name='Hybrid Theory', cover_art=r'albums\hybrid_theory.jpg'
+        )
+
+        widget_settings = {
+            'fields': ('name',),
+            'id': 'pk',
+            'limit': 5,
+            'value': lambda o: unicode(o),
+            'label': lambda o: unicode(o),
+            'queryset': models.Band.objects.all(),
+            }
+        w = AutocompleteWidget(widget_settings)
+        field_name = 'test'
+        expected = "\n".join((
+            '<input type="hidden" name="test" value="%(band_pk)s" id="id_hidden_%(field_name)s" />',
+            '<input type="text" value="%(band_name)s" />',
+            '<script type="text/javascript">django.jQuery("#id_%(field_name)s").djangoautocomplete({"force_selection": true, "multiple": %(multiple)s, "source": "../autocomplete/%(field_name)s/"});</script>',
+            ''
+            )) % {'field_name': field_name,
+                  'band_pk': band.pk,
+                  'band_name': band.name,
+                  'multiple': 'false',
+                  }
+        self.assertEqual(
+            conditional_escape(w.render('test', band.pk, attrs={})),
+            expected,
+        )
+
+
+class MultipleAutocompleteWidgetTest(DjangoTestCase):
+    fixtures = ["admin-widgets-users.xml"]
+    admin_root = '/widget_admin'
+
+    def setUp(self):
+        band = models.Band.objects.create(name='Linkin Park')
+        band.album_set.create(
+            name='Hybrid Theory', cover_art=r'albums\hybrid_theory.jpg'
+        )
+        band2 = models.Band.objects.create(name='Johnny Cash')
+        band2.album_set.create(
+            name='At San Quentin'
+        )
+        self.bands = (band, band2)
+    
+    def _get_expected(self, name, bands):
+        bands = [] if bands is None else bands
+        expected = [
+            '<input type="hidden" name="%(field_name)s" value="%(band_pks)s" id="id_hidden_%(field_name)s" />',
+            '<input type="text" value="" />',  # input is for data entry only
+            ]
+        if bands:                
+                expected += ['<ul class="ui-autocomplete-values">']
+                expected += ['<li>%s</li>' % b.name for b in bands]
+                expected += ['</ul>']
+        expected += [
+            '<script type="text/javascript">django.jQuery("#id_%(field_name)s").djangoautocomplete({"force_selection": true, "multiple": %(multiple)s, "source": "../autocomplete/%(field_name)s/"});</script>',
+            ''
+            ]
+        expected = "\n".join(expected) % {
+            'field_name': name,
+            'band_pks': ','.join([str(b.pk) for b in bands]),
+            'multiple': 'true',
+            }
+        return expected
+
+    def test_render(self):
+        bands = self.bands
+
+        widget_settings = {
+            'fields': ('name',),
+            'id': 'pk',
+            'limit': 5,
+            'value': lambda o: unicode(o),
+            'label': lambda o: unicode(o),
+            'queryset': models.Band.objects.all(),
+            }
+
+        w = MultipleAutocompleteWidget(widget_settings)
+        expected_multiple = self._get_expected('test_multiple', bands)
+        self.assertEqual(
+            conditional_escape(w.render('test_multiple', [b.pk for b in bands], attrs={})),
+            expected_multiple,
+        )
+
+        w = MultipleAutocompleteWidget(widget_settings)
+        bands_just_one = bands[:1]
+        expected_single = self._get_expected('test_single', bands_just_one)
+        self.assertEqual(
+            conditional_escape(w.render('test_single', [b.pk for b in bands_just_one], attrs={})),
+            expected_single,
+        )
+
+        w = MultipleAutocompleteWidget(widget_settings)
+        bands_none = None
+        expected_none = self._get_expected('test_none', bands_none)
+        self.assertEqual(
+            conditional_escape(w.render('test_none', bands_none, attrs={})),
+            expected_none,
+        )
+
+
+    def test_autocomplete_lookup(self):
+        band = self.bands[1]
+        self.client.login(username="super", password="secret")
+        response = self.client.get('%s/admin_widgets/album/autocomplete/band/?term=johnny' % self.admin_root )
+        self.assertEqual(simplejson.loads(response.content),
+                         [{'id': band.id, 
+                           'value': band.name,
+                           'label': band.name}])
+        response = self.client.get('%s/admin_widgets/album/autocomplete/band/?term=a' % self.admin_root )
+        self.assertEqual(simplejson.loads(response.content),
+                         [{'id': band.id, 
+                           'value': band.name,
+                           'label': band.name} for band in self.bands])
+        response = self.client.get('%s/admin_widgets/album/autocomplete/band/?term=%s&by_id=1' % (self.admin_root, band.id ))
+        self.assertEqual(simplejson.loads(response.content),
+                         [{'id': band.id, 
+                           'value': band.name,
+                           'label': band.name}])
+        

tests/regressiontests/admin_widgets/widgetadmin.py

 class EventAdmin(admin.ModelAdmin):
     raw_id_fields = ['band']
 
+
+class AlbumAdmin(admin.ModelAdmin):
+    autocomplete_fields = {
+        'band': { 'fields': ('name',), 'id': 'pk' }
+            }
+
 site = WidgetAdmin(name='widget-admin')
 
 site.register(models.User)
+site.register(models.Album, AlbumAdmin)
 site.register(models.Car, CarAdmin)
 site.register(models.CarTire, CarTireAdmin)
 site.register(models.Event, EventAdmin)