diff --git a/AUTHORS b/AUTHORS
index 8224cda..443ba92 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -90,6 +90,7 @@ answer newbie questions, and generally made Django that much better:
     Mark Biggers <biggers@utsl.com>
     Paul Bissex <http://e-scribe.com/>
     Simon Blanchard
+    Sean Bleier <sebleier@gmail.com>
     David Blewett <david@dawninglight.net>
     Matt Boersma <matt@sprout.org>
     boobsd@gmail.com
@@ -227,6 +228,7 @@ answer newbie questions, and generally made Django that much better:
     Scot Hacker <shacker@birdhouse.org>
     dAniel hAhler
     hambaloney
+    Chuck Harmston <chuck@chuckharmston.com>
     Brian Harring <ferringb@gmail.com>
     Brant Harris
     Ronny Haryanto <http://ronny.haryan.to/>
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index 3a0ad74..3deb0f1 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -163,6 +163,8 @@ class BaseModelAdmin(object):
                 'class': get_ul_class(self.radio_fields[db_field.name]),
             })
             kwargs['empty_label'] = db_field.blank and _('None') or None
+        elif db_field.name in self.filter_vertical:
+            kwargs['widget'] = widgets.FilteredSelectSingle(db_field.verbose_name)
 
         return db_field.formfield(**kwargs)
 
@@ -1375,7 +1377,7 @@ class InlineModelAdmin(BaseModelAdmin):
         if self.prepopulated_fields:
             js.extend(['urlify.js', 'prepopulate.min.js'])
         if self.filter_vertical or self.filter_horizontal:
-            js.extend(['SelectBox.js', 'SelectFilter2.js'])
+            js.extend(['SelectBox.js', 'SelectFilter2.js', 'fkfilter.js'])
         return forms.Media(js=[static('admin/js/%s' % url) for url in js])
 
     def get_formset(self, request, obj=None, **kwargs):
diff --git a/django/contrib/admin/static/admin/css/widgets.css b/django/contrib/admin/static/admin/css/widgets.css
index 2989f2f..451f3e9 100644
--- a/django/contrib/admin/static/admin/css/widgets.css
+++ b/django/contrib/admin/static/admin/css/widgets.css
@@ -5,6 +5,10 @@
     float: left;
 }
 
+.selector-single{
+	width: 282px !important;
+}
+
 .selector select {
     width: 270px;
     height: 17.2em;
diff --git a/django/contrib/admin/validation.py b/django/contrib/admin/validation.py
index 733f89d..500bfcb 100644
--- a/django/contrib/admin/validation.py
+++ b/django/contrib/admin/validation.py
@@ -323,9 +323,9 @@ def validate_base(cls, model):
         check_isseq(cls, 'filter_vertical', cls.filter_vertical)
         for idx, field in enumerate(cls.filter_vertical):
             f = get_field(cls, model, opts, 'filter_vertical', field)
-            if not isinstance(f, models.ManyToManyField):
+            if not isinstance(f, (models.ManyToManyField, models.ForeignKey)):
                 raise ImproperlyConfigured("'%s.filter_vertical[%d]' must be "
-                    "a ManyToManyField." % (cls.__name__, idx))
+                    "a ManyToManyField or ForeignKey." % (cls.__name__, idx))
 
     # filter_horizontal
     if hasattr(cls, 'filter_horizontal'):
diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py
index 0d1f2a9..0bd1b0c 100644
--- a/django/contrib/admin/widgets.py
+++ b/django/contrib/admin/widgets.py
@@ -15,6 +15,36 @@ from django.utils.safestring import mark_safe
 from django.utils.encoding import force_unicode
 
 
+class FilteredSelectSingle(forms.Select):
+    """
+    A Select with a JavaScript filter interface.
+    """
+    #class Media:
+    #    js = (settings.ADMIN_MEDIA_PREFIX + "js/fkfilter.js",)
+    # TODO: mimic newer 'def media' below...
+    @property
+    def media(self):
+        js = ["core.js", "fkfilter.js"]
+        return forms.Media(js=[static("admin/js/%s" % path) for path in js])
+
+    def __init__(self, verbose_name, attrs=None, choices=()):
+        self.verbose_name = verbose_name
+        super(FilteredSelectSingle, self).__init__(attrs, choices)
+
+    def render(self, name, value, attrs={}, choices=()):
+        attrs['class'] = 'selectfilter'
+        output = [super(FilteredSelectSingle, self).render(
+            name, value, attrs, choices)]
+        output.append((
+            '<script type="text/javascript">'
+            'django.jQuery(document).ready(function(){'
+            'django.jQuery("#id_%s").fk_filter("%s")'
+            '});'
+            '</script>'
+        ) % (name, self.verbose_name.replace('"', '\\"'),));
+        return mark_safe(u''.join(output))
+
+
 class FilteredSelectMultiple(forms.SelectMultiple):
     """
     A SelectMultiple with a JavaScript filter interface.
diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt
index 2590243..c218036 100644
--- a/docs/ref/contrib/admin/index.txt
+++ b/docs/ref/contrib/admin/index.txt
@@ -303,7 +303,10 @@ subclass::
 
     Same as :attr:`~ModelAdmin.filter_horizontal`, but uses a vertical display
     of the filter interface with the box of unselected options appearing above
-    the box of selected options.
+    the box of selected options  You can also add a 
+    :class:`~django.db.models.ForeignKey` to this list and a text bo will
+    allow you to narrow your search for a particular object through a
+    potentially large list of options.
 
 .. attribute:: ModelAdmin.form
 
diff --git a/tests/regressiontests/admin_widgets/tests.py b/tests/regressiontests/admin_widgets/tests.py
index 08a1a59..186a602 100644
--- a/tests/regressiontests/admin_widgets/tests.py
+++ b/tests/regressiontests/admin_widgets/tests.py
@@ -190,6 +190,14 @@ class AdminForeignKeyRawIdWidget(DjangoTestCase):
         self.assertEqual(lookup1, lookup2)
 
 
+class FilteredSelectSingleWidgetTest(DjangoTestCase):
+    def test_render(self):
+        w = widgets.FilteredSelectSingle('test')
+        self.assertEqual(
+            conditional_escape(w.render('test', 'test')),
+            '<select name="test" class="selectfilter">\n</select><script type="text/javascript">django.jQuery(document).ready(function(){django.jQuery("#id_test").fk_filter("test")});</script>'
+        )
+
 class FilteredSelectMultipleWidgetTest(DjangoTestCase):
     def test_render(self):
         w = widgets.FilteredSelectMultiple('test', False)
diff --git a/tests/regressiontests/modeladmin/tests.py b/tests/regressiontests/modeladmin/tests.py
index 9b60945..99092be 100644
--- a/tests/regressiontests/modeladmin/tests.py
+++ b/tests/regressiontests/modeladmin/tests.py
@@ -8,7 +8,8 @@ from django.contrib.admin.options import (ModelAdmin, TabularInline,
     HORIZONTAL, VERTICAL)
 from django.contrib.admin.sites import AdminSite
 from django.contrib.admin.validation import validate
-from django.contrib.admin.widgets import AdminDateWidget, AdminRadioSelect
+from django.contrib.admin.widgets import AdminDateWidget, AdminRadioSelect, \
+     FilteredSelectSingle
 from django.contrib.admin import (SimpleListFilter,
      BooleanFieldListFilter)
 from django.core.exceptions import ImproperlyConfigured
@@ -493,6 +494,38 @@ class ModelAdminTests(TestCase):
             list(ma.get_formsets(request))[0]().forms[0].fields.keys(),
             ['extra', 'transport', 'id', 'DELETE', 'main_band'])
 
+    def test_filter_vertical_foreignkey(self):
+        class ConcertModelAdmin(ModelAdmin):
+            filter_vertical = ('main_band',)
+
+        cma = ConcertModelAdmin(Concert, self.site)
+        cmafa = cma.get_form(request)
+
+        self.assertEqual(type(cmafa.base_fields['main_band'].widget.widget),
+            FilteredSelectSingle)
+        self.assertEqual(type(cmafa.base_fields['opening_band'].widget.widget),
+            Select)
+
+        self.assertEqual(
+            list(cmafa.base_fields['main_band'].widget.choices),
+            [(u'', u'---------'), (self.band.id, u'The Doors')])
+
+        self.assertEqual(
+            type(cmafa.base_fields['opening_band'].widget.widget), Select)
+        self.assertEqual(
+            list(cmafa.base_fields['opening_band'].widget.choices),
+            [(u'', u'---------'), (self.band.id, u'The Doors')])
+
+        self.assertEqual(type(cmafa.base_fields['day'].widget), Select)
+        self.assertEqual(list(cmafa.base_fields['day'].widget.choices),
+            [('', '---------'), (1, 'Fri'), (2, 'Sat')])
+
+        self.assertEqual(type(cmafa.base_fields['transport'].widget),
+            Select)
+        self.assertEqual(
+            list(cmafa.base_fields['transport'].widget.choices),
+            [('', '---------'), (1, 'Plane'), (2, 'Train'), (3, 'Bus')])
+
 
 class ValidationTests(unittest.TestCase):
     def test_validation_only_runs_in_debug(self):
