diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index 389a73fa93..e4fbf0005d 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -1,5 +1,6 @@
from django import oldforms, template
from django import newforms as forms
+from django.contrib.admin import widgets
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
from django.db import models
from django.http import Http404, HttpResponse, HttpResponseRedirect
@@ -195,6 +196,19 @@ class ModelAdmin(object):
for fs in self.fieldsets(request):
yield fs
+ def formfield_for_dbfield(self, db_field, **kwargs):
+ """
+ Hook for specifying the form Field instance for a given database Field
+ instance.
+
+ If kwargs are given, they're passed to the form Field's constructor.
+ """
+ # For filter_interface ManyToManyFields, use a special Widget.
+ if isinstance(db_field, models.ManyToManyField) and db_field.rel.filter_interface:
+ widget = widgets.FilteredSelectMultiple(db_field.verbose_name, db_field.rel.filter_interface-1)
+ return db_field.formfield(widget=widget)
+ return db_field.formfield(**kwargs)
+
def has_add_permission(self, request):
"Returns True if the given request has permission to add an object."
opts = self.opts
@@ -238,7 +252,7 @@ class ModelAdmin(object):
# Object list will give 'Permission Denied', so go back to admin home
post_url = '../../../'
- ModelForm = forms.form_for_model(model)
+ ModelForm = forms.form_for_model(model, formfield_callback=self.formfield_for_dbfield)
if request.POST:
new_data = request.POST.copy()
@@ -303,7 +317,7 @@ class ModelAdmin(object):
except model.DoesNotExist:
raise Http404('%s object with primary key %r does not exist' % (model_name, escape(object_id)))
- ModelForm = forms.form_for_instance(obj)
+ ModelForm = forms.form_for_instance(obj, formfield_callback=self.formfield_for_dbfield)
if request.POST:
new_data = request.POST.copy()
diff --git a/django/contrib/admin/templatetags/admin_modify.py b/django/contrib/admin/templatetags/admin_modify.py
index 7e9eb41f59..ab700d369d 100644
--- a/django/contrib/admin/templatetags/admin_modify.py
+++ b/django/contrib/admin/templatetags/admin_modify.py
@@ -160,16 +160,6 @@ def auto_populated_field_script(auto_pop_fields, change = False):
return ''.join(t)
auto_populated_field_script = register.simple_tag(auto_populated_field_script)
-def filter_interface_script_maybe(bound_field):
- f = bound_field.field
- if f.rel and isinstance(f.rel, models.ManyToManyRel) and f.rel.filter_interface:
- return '\n' % (
- f.name, f.verbose_name.replace('"', '\\"'), f.rel.filter_interface-1, settings.ADMIN_MEDIA_PREFIX)
- else:
- return ''
-filter_interface_script_maybe = register.simple_tag(filter_interface_script_maybe)
-
def field_widget(parser, token):
bits = token.contents.split()
if len(bits) != 2:
diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py
new file mode 100644
index 0000000000..96346011ce
--- /dev/null
+++ b/django/contrib/admin/widgets.py
@@ -0,0 +1,27 @@
+"""
+Form Widget classes specific to the Django admin site.
+"""
+
+from django import newforms as forms
+
+class FilteredSelectMultiple(forms.SelectMultiple):
+ """
+ A SelectMultiple with a JavaScript filter interface.
+
+ Note that the resulting JavaScript assumes that the SelectFilter2.js
+ library and its dependencies have been loaded in the HTML page.
+ """
+ def __init__(self, verbose_name, is_stacked, attrs=None, choices=()):
+ self.verbose_name = verbose_name
+ self.is_stacked = is_stacked
+ super(FilteredSelectMultiple, self).__init__(attrs, choices)
+
+ def render(self, name, value, attrs=None, choices=()):
+ from django.conf import settings
+ output = [super(FilteredSelectMultiple, self).render(name, value, attrs, choices)]
+ output.append('\n' % \
+ (name, self.verbose_name.replace('"', '\\"'), int(self.is_stacked), settings.ADMIN_MEDIA_PREFIX))
+ return ''.join(output)