Commits

za...@bcc190cf-cafb-0310-a4f2-bffc1f526a37  committed 8d9c62e

[soc2009/admin-ui] Moving the autocomplete view to a more appropriate location under the ModelAdmin

  • Participants
  • Parent commits 3f665ef
  • Branches soc2009/admin-ui

Comments (0)

Files changed (6)

File django/contrib/admin/options.py

+import operator
 from django import forms, template
 from django.forms.formsets import all_valid
 from django.forms.models import modelform_factory, modelformset_factory, inlineformset_factory
 from django.core.exceptions import PermissionDenied
 from django.db import models, transaction
 from django.db.models.fields import BLANK_CHOICE_DASH
+from django.db.models.query import QuerySet
 from django.http import Http404, HttpResponse, HttpResponseRedirect
 from django.shortcuts import get_object_or_404, render_to_response
 from django.utils.datastructures import SortedDict
 from django.utils.text import capfirst, get_text_list
 from django.utils.translation import ugettext as _
 from django.utils.translation import ungettext, ugettext_lazy
-from django.utils.encoding import force_unicode
+from django.utils.encoding import force_unicode, smart_str
 try:
     set
 except NameError:
             url(r'^add/$',
                 wrap(self.add_view),
                 name='%s_%s_add' % info),
+            url(r'^autocomplete/(?P<field>[\w-]+)/$',
+                wrap(self.autocomplete_view),
+                name='%s_%s_autocomplete' % info),
             url(r'^(.+)/history/$',
                 wrap(self.history_view),
                 name='%s_%s_history' % info),
             else:
                 return HttpResponseRedirect(".")
 
+    def autocomplete_view(self, request, field, extra_content=None):
+        """
+        Used by the JQuery Autocomplete plugin to do searches on fields of the related model
+        """
+        query = request.GET.get('q', None)
+        
+        if field not in self.autocomplete_fields or query is None:
+            raise Http404
+        
+        related = getattr(self.model, field)
+        rel_model = related.field.rel.to
+        queryset = rel_model._default_manager.all()
+        search_fields = self.autocomplete_fields[field]
+        
+        def construct_search(field_name):
+            # use different lookup methods depending on the notation
+            if field_name.startswith('^'):
+                return "%s__istartswith" % field_name[1:]
+            elif field_name.startswith('='):
+                return "%s__iexact" % field_name[1:]
+            elif field_name.startswith('@'):
+                return "%s__search" % field_name[1:]
+            else:
+                return "%s__icontains" % field_name
+        
+        for bit in query.split():
+            or_queries = [models.Q(**{construct_search(
+                smart_str(field_name)): smart_str(bit)})
+                    for field_name in search_fields]
+            other_qs = QuerySet(rel_model)
+            other_qs.dup_select_related(queryset)
+            other_qs = other_qs.filter(reduce(operator.or_, or_queries))
+            queryset = queryset & other_qs
+        
+        return HttpResponse(''.join([u'%s|%s\n' % (unicode(f), f.pk) for f in queryset]))
+    
     def add_view(self, request, form_url='', extra_context=None):
         "The 'add' admin view for this model."
         model = self.model

File django/contrib/admin/sites.py

             url(r'^jsi18n/$',
                 wrap(self.i18n_javascript, cacheable=True),
                 name='jsi18n'),
-            url(r'^foreignkey_autocomplete/$',
-                'django.contrib.admin.views.autocomplete.foreignkey_autocomplete'),
             url(r'^r/(?P<content_type_id>\d+)/(?P<object_id>.+)/$',
                 'django.views.defaults.shortcut'),
             url(r'^(?P<app_label>\w+)/$',

File django/contrib/admin/templates/widget/foreignkey_searchinput.html

         $('#id_{{ name }}').val('');
         $('#lookup_{{ name }}').val('');
     };
-    function lookup(query) {
-        $.get('{{ search_path }}', {
-            'search_fields': '{{ search_fields }}',
-            'app_label': '{{ app_label }}',
-            'model_name': '{{ model_name }}',
-            'object_pk': query
-        }, function(data){
-            $('#lookup_{{ name }}').val(data);
-            {{ name }}_value = query;
-        });
-    };
-    $('#id_{{ name }}').bind(($.browser.opera ? "keypress" : "keyup"), function(event) {
-        if ($(this).val()) {
-            if (event.keyCode == 27) {
-                reset();
-            } else {
-                lookup($(this).val());
-            };
-        };
-    });
-    $('#lookup_{{ name }}').autocomplete('{{ search_path }}', {
-        extraParams: {
-            'search_fields': '{{ search_fields }}',
-            'app_label': '{{ app_label }}',
-            'model_name': '{{ model_name }}'
-        }
-    }).result(function(event, data, formatted) {
-        if (data) {
-            $('#id_{{ name }}').val(data[1]);
+
+    $('#lookup_{{ name }}').autocomplete('{{ search_path }}').result(
+        function(event, data, formatted) {
+            if (data) {
+                $('#id_{{ name }}').val(data[1]);
         }
     }).keyup(function(event){
         if (event.keyCode == 27) {

File django/contrib/admin/templates/widget/m2m_searchinput.html

     // Show lookup input
     $("#lookup_{{ name }}").show();
     
-    function lookup(query) {
-        $.get('{{ search_path }}', {
-            'search_fields': '{{ search_fields }}',
-            'app_label': '{{ app_label }}',
-            'model_name': '{{ model_name }}',
-            'object_pk': query
-        }, function(data){
-            $('#lookup_{{ name }}').val(data);
-        });
-    };
     $('#lookup_{{ name }}').autocomplete('{{ search_path }}', {
-        extraParams: {
-            'search_fields': '{{ search_fields }}',
-            'app_label': '{{ app_label }}',
-            'model_name': '{{ model_name }}'
-        },
         multiple: true,
         mustMatch: true
     }).result(function(event, data, formatted) {

File django/contrib/admin/views/autocomplete.py

-import operator
-from django.db import models
-from django.db.models.query import QuerySet
-from django.utils.encoding import smart_str
-from django.http import HttpResponse, HttpResponseNotFound
-from django.conf import settings
-from django.contrib.admin.views.decorators import staff_member_required
- 
-def foreignkey_autocomplete(request, related_string_functions=None):
-    """
-    Searches in the fields of the given related model and returns the
-    result as a simple string to be used by the jQuery Autocomplete plugin
-    """
-    if related_string_functions is None:
-        related_string_functions = getattr(settings,
-            'DJANGO_EXTENSIONS_FOREIGNKEY_AUTOCOMPLETE_STRING_FUNCTIONS', {})
-    query = request.GET.get('q', None)
-    app_label = request.GET.get('app_label', None)
-    model_name = request.GET.get('model_name', None)
-    search_fields = request.GET.get('search_fields', None)
-    object_pk = request.GET.get('object_pk', None)
-    try:
-        to_string_function = related_string_functions[model_name]
-    except KeyError:
-        to_string_function = lambda x: unicode(x)
-    if search_fields and app_label and model_name and (query or object_pk):
-        def construct_search(field_name):
-            # use different lookup methods depending on the notation
-            if field_name.startswith('^'):
-                return "%s__istartswith" % field_name[1:]
-            elif field_name.startswith('='):
-                return "%s__iexact" % field_name[1:]
-            elif field_name.startswith('@'):
-                return "%s__search" % field_name[1:]
-            else:
-                return "%s__icontains" % field_name
-        model = models.get_model(app_label, model_name)
-        queryset = model._default_manager.all()
-        data = ''
-        if query:
-            for bit in query.split():
-                or_queries = [models.Q(**{construct_search(
-                    smart_str(field_name)): smart_str(bit)})
-                        for field_name in search_fields.split(',')]
-                other_qs = QuerySet(model)
-                other_qs.dup_select_related(queryset)
-                other_qs = other_qs.filter(reduce(operator.or_, or_queries))
-                queryset = queryset & other_qs
-            data = ''.join([u'%s|%s\n' % (
-                to_string_function(f), f.pk) for f in queryset])
-        elif object_pk:
-            try:
-                obj = queryset.get(pk=object_pk)
-            except:
-                pass
-            else:
-                data = to_string_function(obj)
-        return HttpResponse(data)
-    return HttpResponseNotFound()
-foreignkey_autocomplete = staff_member_required(foreignkey_autocomplete)

File django/contrib/admin/widgets.py

     """
     # Set in subclass to render the widget with a different template
     widget_template = 'widget/foreignkey_searchinput.html'
-    # Set this to the path of the search view
-    search_path = '../../../foreignkey_autocomplete/'
  
     class Media:
         css = {
         self.search_fields = search_fields
         super(ForeignKeySearchInput, self).__init__(rel, attrs)
  
+    def get_search_path(self, name):
+        return '../autocomplete/%s/' % name
+    
     def render(self, name, value, attrs=None):
         if attrs is None:
             attrs = {}
             'url': url,
             'related_url': related_url,
             'admin_media_prefix': settings.ADMIN_MEDIA_PREFIX,
-            'search_path': self.search_path,
+            'search_path': self.get_search_path(name),
             'search_fields': ','.join(self.search_fields),
             'model_name': model_name,
             'app_label': app_label,
     """
     # Set in subclass to render the widget with a different template
     widget_template = 'widget/m2m_searchinput.html'
-    # Set this to the path of the search view
-    search_path = '../../../foreignkey_autocomplete/'
  
     class Media:
         css = {
         objs = self.rel.to._default_manager.filter(**{key + '__in': value.split(',')})
         return ','.join([str(o) for o in objs])
         
+    def get_search_path(self, name):
+        return '../autocomplete/%s/' % name
     
     def render(self, name, value, attrs=None):
         if attrs is None:
             'url': url,
             'related_url': related_url,
             'admin_media_prefix': settings.ADMIN_MEDIA_PREFIX,
-            'search_path': self.search_path,
+            'search_path': self.get_search_path(name),
             'search_fields': ','.join(self.search_fields),
             'model_name': model_name,
             'app_label': app_label,