Source

django / django / contrib / admin / widgets.py

The default branch has multiple heads

Diff from to

django/contrib/admin/widgets.py

     template_with_clear = (u'<span class="clearable-file-input">%s</span>'
                            % forms.ClearableFileInput.template_with_clear)
 
-def _get_search_icon(model, name, value, params, attrs):
-    related_url = '../../../%s/%s/' % (model._meta.app_label, model._meta.object_name.lower())
+def _get_search_icon(model, name, value, params, attrs, admin_site=None):
+    info = (model._meta.app_label, model._meta.object_name.lower())
+    try:
+        options = {'current_app': admin_site.name} if admin_site else {}
+        related_url = reverse('admin:%s_%s_changelist' % info, **options)
+    except NoReverseMatch:
+        path_to_root = admin_site.root_path if admin_site else '../../../'
+        info = (path_to_root, model._meta.app_label, model._meta.object_name.lower())
+        related_url = '%s%s/%s/' % info
     if params:
         url = '?' + '&amp;'.join(['%s=%s' % (k, v) for k, v in params.items()])
     else:
     A Widget for displaying ForeignKeys in the "raw_id" interface rather than
     in a <select> box.
     """
-    def __init__(self, rel, attrs=None, using=None):
+    def __init__(self, rel, attrs=None, using=None, admin_site=None):
         self.rel = rel
         self.db = using
+        self.admin_site = admin_site
         super(ForeignKeyRawIdWidget, self).__init__(attrs)
 
     def render(self, name, value, attrs=None):
             attrs['class'] = 'vForeignKeyRawIdAdminField' # The JavaScript looks for this hook.
         output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)]
 
-        output += _get_search_icon(self.rel.to, name, value, self.url_parameters(), attrs)
+        admin_site = self.admin_site if hasattr(self, 'admin_site') else None
+        output += _get_search_icon(self.rel.to, name, value, self.url_parameters(), attrs, admin_site=admin_site)
         if value:
             output.append(self.label_for_value(value))
         return mark_safe(u''.join(output))
             settings.ADMIN_MEDIA_PREFIX + 'js/admin/autocomplete.js',
         )
 
-    def __init__(self, settings, attrs=None, using=None, **js_options):
+    def __init__(self, settings, attrs=None, using=None, admin_site=None, **js_options):
         self.settings = settings
         self.db = using
+        self.admin_site = admin_site
         self.js_options = dict(
             source = settings.get('source'),
             multiple = settings.get('multiple', False),
         super(AutocompleteWidget, self).__init__(attrs)
 
     def get_autocomplete_url(self, name):
-        return '../autocomplete/%s/' % name
+        model = self.settings.get('parent_model', None)
+        if model:
+            info = (model._meta.app_label, model._meta.object_name.lower())
+            # look for the real related name incase this is used by a formset
+            qs = self.settings['queryset']
+            if self.settings.get('multiple', False):
+                names = [o.name for o in model._meta.many_to_many if o.rel.to == qs.model]
+                # formsset forms are named "form-##-<name>", hence the "-%s"
+                clean_name = [n for n in names if name == n or name.endswith("-%s" % n)][0]
+            else:
+                # @@@TODO check for multiple references to same model 
+                #         as above with m2m names?
+                clean_name = qs.model._meta.module_name
+            try:
+                url = reverse('admin:%s_%s_autocomplete' % info, args=[clean_name], current_app=self.admin_site.name)
+            except NoReverseMatch:
+                info = (self.admin_site.root_path, model._meta.app_label, model._meta.object_name.lower(), clean_name)
+                url = '%s%s/%s/autocomplete/%s/' % info
+        else:
+            url = '../autocomplete/%s/' % name
+        return url
 
     def render(self, name, value, attrs=None, hattrs=None, initial_objects=u''):
-        if value is None:
+        if value is None or (type(value) != int and len(value) == 0):
             value = ''
         hidden_id = 'id_hidden_%s' % name
         hidden_attrs = self.build_attrs(type='hidden', name=name, value=value, id=hidden_id)
         if self.settings.get('show_search'):
             target_key = self.settings.get('id')
             search_icon = _get_search_icon(self.settings.get('queryset').model, 
-                                           name, value, {'t': target_key}, attrs)
+                                           name, value, {'t': target_key}, 
+                                           attrs, admin_site=self.admin_site)
             search_icon = u''.join(search_icon) + '\n'
         else:
             search_icon = u''
 class MultipleAutocompleteWidget(AutocompleteWidget):
 
     def __init__(self, settings, attrs=None, using=None, **js_options):
-        js_options['multiple'] = True
+        settings['multiple'] = True
         super(MultipleAutocompleteWidget, self).__init__(settings, attrs,
             using, **js_options)
 
         rel_to = self.rel.to
         info = (rel_to._meta.app_label, rel_to._meta.object_name.lower())
         try:
-            related_url = reverse('admin:%s_%s_add' % info, current_app=self.admin_site.name)
+            options = {'current_app': self.admin_site.name} if hasattr(self, 'admin_site') else {} 
+            related_url = reverse('admin:%s_%s_add' % info, **options)
         except NoReverseMatch:
-            info = (self.admin_site.root_path, rel_to._meta.app_label, rel_to._meta.object_name.lower())
+            path_to_root = self.admin_site.root_path if hasattr(self, 'admin_site') else '../../../'
+            info = (path_to_root, rel_to._meta.app_label, rel_to._meta.object_name.lower())
             related_url = '%s%s/%s/add/' % info
         self.widget.choices = self.choices
         output = [self.widget.render(name, value, *args, **kwargs)]