Repository: django-extensions/django-extensions

Issue:
limit queryset for ForeinKeyAutocompleteAdmin field
Make use of `limit` GET parameter and limit queryset.

If no limit is set, it will default to 10.



Discussion:

Resolving patch:
diff --git a/django_extensions/admin/__init__.py b/django_extensions/admin/__init__.py
--- a/django_extensions/admin/__init__.py
+++ b/django_extensions/admin/__init__.py
@@ -1,13 +1,6 @@
 #
 # Autocomplete feature for admin panel
 #
-# Most of the code has been written by Jannis Leidel and was updated a bit
-# for django_extensions.
-# http://jannisleidel.com/2008/11/autocomplete-form-widget-foreignkey-model-fields/
-#
-# to_string_function, Satchmo adaptation and some comments added by emes
-# (Michal Salaban)
-#
 
 import six
 import operator
@@ -52,16 +45,20 @@ class ForeignKeyAutocompleteAdmin(ModelAdmin):
          take target model instance as only argument and return string
          representation. By default __unicode__() method of target
          object is used.
+
+    And also an optional additional field to set the limit on the
+    results returned by the autocomplete query. You can set this integer
+    value in your settings file using FOREIGNKEY_AUTOCOMPLETE_LIMIT or
+    you can set this per ForeignKeyAutocompleteAdmin basis. If any value
+    is set the results will not be limited.
     """
 
     related_search_fields = {}
     related_string_functions = {}
+    autocomplete_limit = settings.get('FOREIGNKEY_AUTOCOMPLETE_LIMIT', None)
 
     def get_urls(self):
-        try:
-            from django.conf.urls import patterns, url
-        except ImportError:  # django < 1.4
-            from django.conf.urls.defaults import patterns, url
+        from django.conf.urls import patterns, url
 
         def wrap(view):
             def wrapper(*args, **kwargs):
@@ -84,10 +81,12 @@ def foreignkey_autocomplete(self, request):
         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 = self.related_string_functions[model_name]
         except KeyError:
             to_string_function = lambda x: x.__unicode__()
+
         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
@@ -106,9 +105,13 @@ def construct_search(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.split(',')]
                     other_qs = QuerySet(model)
-                    other_qs.dup_select_related(queryset)
+                    other_qs.query.select_related = queryset.query.select_related
                     other_qs = other_qs.filter(reduce(operator.or_, or_queries))
                     queryset = queryset & other_qs
+
+                if self.autocomplete_limit:
+                    queryset = queryset[:self.autocomplete_limit]
+
                 data = ''.join([six.u('%s|%s\n') % (to_string_function(f), f.pk) for f in queryset])
             elif object_pk:
                 try:
diff --git a/django_extensions/admin/widgets.py b/django_extensions/admin/widgets.py
--- a/django_extensions/admin/widgets.py
+++ b/django_extensions/admin/widgets.py
@@ -1,13 +1,9 @@
 import six
-import django
+
 from django import forms
-from django.conf import settings
 from django.contrib.admin.sites import site
 from django.utils.safestring import mark_safe
-if django.get_version() >= "1.4":
-    from django.utils.text import Truncator
-else:
-    from django.utils.text import truncate_words
+from django.utils.text import Truncator
 from django.template.loader import render_to_string
 from django.contrib.admin.widgets import ForeignKeyRawIdWidget
 
@@ -34,17 +30,12 @@ def _media(self):
     def label_for_value(self, value):
         key = self.rel.get_related_field().name
         obj = self.rel.to._default_manager.get(**{key: value})
-        if django.get_version() >= "1.4":
-            return Truncator(obj).words(14, truncate='...')
-        else:
-            return truncate_words(obj, 14)
+
+        return Truncator(obj).words(14, truncate='...')
 
     def __init__(self, rel, search_fields, attrs=None):
         self.search_fields = search_fields
-        if django.get_version() >= "1.4":
-            super(ForeignKeySearchInput, self).__init__(rel, site, attrs)
-        else:
-            super(ForeignKeySearchInput, self).__init__(rel, attrs)
+        super(ForeignKeySearchInput, self).__init__(rel, site, attrs)
 
     def render(self, name, value, attrs=None):
         if attrs is None:
@@ -59,31 +50,26 @@ def render(self, name, value, attrs=None):
             url = '?' + '&amp;'.join(['%s=%s' % (k, v) for k, v in params.items()])
         else:
             url = ''
+
         if not 'class' in attrs:
             attrs['class'] = 'vForeignKeyRawIdAdminField'
         # Call the TextInput render method directly to have more control
         output = [forms.TextInput.render(self, name, value, attrs)]
+
         if value:
             label = self.label_for_value(value)
         else:
             label = six.u('')
 
-        try:
-            admin_media_prefix = settings.ADMIN_MEDIA_PREFIX
-        except AttributeError:
-            admin_media_prefix = settings.STATIC_URL + "admin/"
-
         context = {
             'url': url,
             'related_url': related_url,
-            'admin_media_prefix': admin_media_prefix,
             'search_path': self.search_path,
             'search_fields': ','.join(self.search_fields),
-            'model_name': model_name,
             'app_label': app_label,
+            'model_name': model_name,
             'label': label,
             'name': name,
-            'pre_django_14': (django.VERSION[:2] < (1, 4)),
         }
         output.append(render_to_string(self.widget_template or (
             'django_extensions/widgets/%s/%s/foreignkey_searchinput.html' % (app_label, model_name),
@@ -91,4 +77,5 @@ def render(self, name, value, attrs=None):
             'django_extensions/widgets/foreignkey_searchinput.html',
         ), context))
         output.reverse()
+
         return mark_safe(six.u('').join(output))


Test patch: