Commits

Ian Struble committed a035b2f

Updated by_id lookups to properly support id other than 'id'.

Made the client-side select handler ignore bogus data. This is not the most desirable behavior but it will works. Also updated the server side of the lookups so the select handler should no longer get the bogus data in the first place.

Example field configuration that was failing:

autocomplete_fields = dict(
char_notrequired = dict(
queryset = User.objects.all(),
fields = ('^email', '^username'),
id = 'email',
value = 'email',
),
)

Thanks to Klemens Mantzos for the testing and bug report.

  • Participants
  • Parent commits e7210b9

Comments (0)

Files changed (6)

django/contrib/admin/media/js/admin/autocomplete.js

         this.element.autocomplete({
             appendTo: this.element.parent(),
             select: function( event, ui ) {
-                var item = ui.item.data ? ui.item.data( "item.autocomplete" ) : ui.item;
+                if (!ui.item) { return; /* unexpected result */ }
+                var item = ui.item['data'] && ui.item.data( "item.autocomplete" ) || ui.item;
                 self.lastSelected = item
                 if (self.options.is_djangoautocomplete === true) {
                     if ( self.options.multiple ) {

django/contrib/admin/options.py

     models.FileField:       {'widget': widgets.AdminFileWidget},
 }
 
-AUTOCOMPLETE_FIELDS_DEFAULTS = dict(
-    limit = 5,
-    value = lambda o: unicode(o),
-    label = lambda o: unicode(o),
-    show_search = True,
-)
+AUTOCOMPLETE_FIELDS_DEFAULTS = {
+    'limit': 5,
+    'value': lambda o: unicode(o),
+    'label': lambda o: unicode(o),
+    'show_search': True,
+}
 
 csrf_protect_m = method_decorator(csrf_protect)
 
         search_fields = settings['fields']
         if request.GET.get('by_id', None) is not None:
             # lookup only via an exact match on id
-            search_fields = ('=id',)
+            search_fields = ('=%s' % settings['id'],)
 
         def construct_search(field_name):
             # use different lookup methods depending on the notation
             or_queries = [models.Q(**{construct_search(
                 smart_str(field_name)): bit})
                     for field_name in search_fields]
-
             queryset = queryset.filter(reduce(operator.or_, or_queries))
         
         data = []
         for o in queryset[:settings['limit']]:
-            data.append(dict(
-                id = getattr(o, settings['id']),
-                value = settings['value'](o),
-                label = settings['label'](o),
-            ))
+            data.append({
+                'id': getattr(o, settings['id']),
+                'value': settings['value'](o),
+                'label': settings['label'](o),
+            })
         
         return HttpResponse(simplejson.dumps(data))
     

docs/ref/contrib/admin/index.txt

     class AuthorAdmin(admin.ModelAdmin):
         date_hierarchy = 'pub_date'
 
+.. versionadded:: 1.3
+
 .. attribute:: ModelAdmin.autocomplete_fields
 
 By default, Django's admin uses a select-box interface (<select>) for fields 
         A tuple of field names used to search for objects associated with 
         ``field_name``. This key is required.
 
+        Example::
+
+            'fields': ('name', '^user__email',),
+
     * ``label``
         A formatting string or subroutine that controls how each choice 
         is displayed in the list of autocomplete choices.
 
         Example::
 
+            'label': 'name'
             'label': '%(name)s [%(gender)s]'
             'label': lambda o: o.name.lower()
 

tests/regressiontests/admin_widgets/models.py

     name = models.CharField(max_length=100)
     birthdate = models.DateTimeField(blank=True, null=True)
     gender = models.CharField(max_length=1, blank=True, choices=[('M','Male'), ('F', 'Female')])
+    user = models.ForeignKey(User, blank=True, null=True)
 
     def __unicode__(self):
         return self.name

tests/regressiontests/admin_widgets/tests.py

         )
         
 
-    def test_autocomplete_lookup(self):
+    def test_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 )
                            'value': band.name,
                            'label': band.name}])
         
+
+    def test_different_id(self):
+        self.client.login(username="super", password="secret")
+        user = models.User.objects.get(username='testser')
+        user.member_set.create(
+            name='Man Named Sue',
+            user=user,
+        )
+        expected = [{'id': user.email, 
+                     'value': user.username,
+                     'label': user.username}]
+        lookup_url = "%s/admin_widgets/member/autocomplete/user/?term=%%s" % self.admin_root
+        lookup_by_id_url = "%s&by_id=1" % lookup_url
+
+        response = self.client.get(lookup_url % user.username[:2])
+        self.assertEqual(simplejson.loads(response.content),
+                         expected)
+        response = self.client.get(lookup_url % user.email[:2])
+        self.assertEqual(simplejson.loads(response.content),
+                         expected)
+        response = self.client.get(lookup_url % user.first_name[:2])
+        self.assertEqual(simplejson.loads(response.content),
+                         expected)
+        response = self.client.get(lookup_by_id_url % user.email)
+        self.assertEqual(simplejson.loads(response.content),
+                         expected)
+        

tests/regressiontests/admin_widgets/widgetadmin.py

         'band': { 'fields': ('name',) }
             }
 
+class MemberAdmin(admin.ModelAdmin):
+    autocomplete_fields = {
+        'user': { 
+            'fields': ('^email', '^username', '^first_name', ),
+            'id': 'email',  # weird id for test_different_id
+            }
+        }
+
+
 site = WidgetAdmin(name='widget-admin')
 
 site.register(models.User)
 site.register(models.Car, CarAdmin)
 site.register(models.CarTire, CarTireAdmin)
 site.register(models.Event, EventAdmin)
+site.register(models.Member, MemberAdmin)