@@ -263,6 +263,81 @@ def _has_changed(self, initial, data):
263263 return True
264264 return False
265265
266+ class ManyToManySearchInput (ManyToManyRawIdWidget ):
267+ """
268+ A Widget for displaying M2Ms in an autocomplete search input
269+ instead in a <select> box.
270+ """
271+ # Set in subclass to render the widget with a different template
272+ widget_template = 'widget/m2m_searchinput.html'
273+ # Set this to the path of the search view
274+ search_path = '../../../foreignkey_autocomplete/'
275+
276+ class Media :
277+ css = {
278+ 'all' : (settings .ADMIN_MEDIA_PREFIX + 'css/jquery.autocomplete.css' ,)
279+ }
280+ js = (
281+ settings .ADMIN_MEDIA_PREFIX + 'js/jquery.js' ,
282+ settings .ADMIN_MEDIA_PREFIX + 'js/jquery.bgiframe.min.js' ,
283+ settings .ADMIN_MEDIA_PREFIX + 'js/jquery.ajaxQueue.js' ,
284+ settings .ADMIN_MEDIA_PREFIX + 'js/jquery.autocomplete.js' ,
285+ )
286+
287+ def __init__ (self , rel , search_fields , attrs = None ):
288+ self .search_fields = search_fields
289+ super (ManyToManySearchInput , self ).__init__ (rel , attrs )
290+
291+ def label_for_value (self , value ):
292+ key = self .rel .get_related_field ().name
293+ objs = self .rel .to ._default_manager .filter (** {key + '__in' : value .split (',' )})
294+ return ',' .join ([str (o ) for o in objs ])
295+
296+
297+ def render (self , name , value , attrs = None ):
298+ if attrs is None :
299+ attrs = {}
300+ output = [super (ManyToManySearchInput , self ).render (name , value , attrs )]
301+ if value :
302+ value = ',' .join ([str (v ) for v in value ])
303+ else :
304+ value = ''
305+ opts = self .rel .to ._meta
306+ app_label = opts .app_label
307+ model_name = opts .object_name .lower ()
308+ related_url = '../../../%s/%s/' % (app_label , model_name )
309+ params = self .url_parameters ()
310+ if params :
311+ url = '?' + '&' .join (['%s=%s' % (k , v ) for k , v in params .items ()])
312+ else :
313+ url = ''
314+ if not attrs .has_key ('class' ):
315+ attrs ['class' ] = 'vM2MRawIdAdminField'
316+ # Call the TextInput render method directly to have more control
317+ output = [forms .TextInput .render (self , name , value , attrs )]
318+ if value :
319+ label = self .label_for_value (value )
320+ else :
321+ label = u''
322+ context = {
323+ 'url' : url ,
324+ 'related_url' : related_url ,
325+ 'admin_media_prefix' : settings .ADMIN_MEDIA_PREFIX ,
326+ 'search_path' : self .search_path ,
327+ 'search_fields' : ',' .join (self .search_fields ),
328+ 'model_name' : model_name ,
329+ 'app_label' : app_label ,
330+ 'label' : label ,
331+ 'name' : name ,
332+ }
333+ output .append (render_to_string (self .widget_template or (
334+ 'templates/widget/%s/%s/m2m_searchinput.html' % (app_label , model_name ),
335+ 'templates/widget/%s/m2m_searchinput.html' % app_label ,
336+ 'templates/widget/m2m_searchinput.html' ,
337+ ), context ))
338+ output .reverse ()
339+ return mark_safe (u'' .join (output ))
340+
266341class RelatedFieldWidgetWrapper (forms .Widget ):
267342 """
268343 This class is a wrapper to a given widget to add the add icon for the
0 commit comments