mirror of
https://github.com/django/django.git
synced 2025-07-04 09:49:12 +00:00
[soc2009/admin-ui] M2M autocomplete, modeled much like the FK autocomplete.
git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/admin-ui@11427 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
2919305e1f
commit
c4dd344a9b
@ -163,6 +163,9 @@ class BaseModelAdmin(object):
|
|||||||
if db_field.name in self.raw_id_fields:
|
if db_field.name in self.raw_id_fields:
|
||||||
kwargs['widget'] = widgets.ManyToManyRawIdWidget(db_field.rel)
|
kwargs['widget'] = widgets.ManyToManyRawIdWidget(db_field.rel)
|
||||||
kwargs['help_text'] = ''
|
kwargs['help_text'] = ''
|
||||||
|
elif db_field.name in self.autocomplete_fields:
|
||||||
|
kwargs['widget'] = widgets.ManyToManySearchInput(db_field.rel,
|
||||||
|
self.autocomplete_fields[db_field.name])
|
||||||
elif db_field.name in (list(self.filter_vertical) + list(self.filter_horizontal)):
|
elif db_field.name in (list(self.filter_vertical) + list(self.filter_horizontal)):
|
||||||
kwargs['widget'] = widgets.FilteredSelectMultiple(db_field.verbose_name, (db_field.name in self.filter_vertical))
|
kwargs['widget'] = widgets.FilteredSelectMultiple(db_field.verbose_name, (db_field.name in self.filter_vertical))
|
||||||
|
|
||||||
|
36
django/contrib/admin/templates/widget/m2m_searchinput.html
Normal file
36
django/contrib/admin/templates/widget/m2m_searchinput.html
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
<textarea id="lookup_{{ name }}" style="display:none;">{{ label }}</textarea>
|
||||||
|
<a href="{{ related_url }}{{ url }}" class="related-lookup" id="lookup_id_{{ name }}" onclick="return showRelatedObjectLookupPopup(this);">
|
||||||
|
<img src="{{ admin_media_prefix }}img/admin/selector-search.gif" width="16" height="16" alt="{% trans "Lookup" %}" />
|
||||||
|
</a>
|
||||||
|
<script type="text/javascript">
|
||||||
|
$(document).ready(function() {
|
||||||
|
// 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,
|
||||||
|
autoFill: true
|
||||||
|
}).result(function(event, data, formatted) {
|
||||||
|
if (data) {
|
||||||
|
$('#id_{{ name }}').val($('#id_{{ name }}').val() + data[1] + ",");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
@ -263,6 +263,81 @@ class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
class ManyToManySearchInput(ManyToManyRawIdWidget):
|
||||||
|
"""
|
||||||
|
A Widget for displaying M2Ms in an autocomplete search input
|
||||||
|
instead in a <select> box.
|
||||||
|
"""
|
||||||
|
# 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 = {
|
||||||
|
'all': (settings.ADMIN_MEDIA_PREFIX + 'css/jquery.autocomplete.css',)
|
||||||
|
}
|
||||||
|
js = (
|
||||||
|
settings.ADMIN_MEDIA_PREFIX + 'js/jquery.js',
|
||||||
|
settings.ADMIN_MEDIA_PREFIX + 'js/jquery.bgiframe.min.js',
|
||||||
|
settings.ADMIN_MEDIA_PREFIX + 'js/jquery.ajaxQueue.js',
|
||||||
|
settings.ADMIN_MEDIA_PREFIX + 'js/jquery.autocomplete.js',
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, rel, search_fields, attrs=None):
|
||||||
|
self.search_fields = search_fields
|
||||||
|
super(ManyToManySearchInput, self).__init__(rel, attrs)
|
||||||
|
|
||||||
|
def label_for_value(self, value):
|
||||||
|
key = self.rel.get_related_field().name
|
||||||
|
objs = self.rel.to._default_manager.filter(**{key + '__in': value.split(',')})
|
||||||
|
return ','.join([str(o) for o in objs])
|
||||||
|
|
||||||
|
|
||||||
|
def render(self, name, value, attrs=None):
|
||||||
|
if attrs is None:
|
||||||
|
attrs = {}
|
||||||
|
output = [super(ManyToManySearchInput, self).render(name, value, attrs)]
|
||||||
|
if value:
|
||||||
|
value = ','.join([str(v) for v in value])
|
||||||
|
else:
|
||||||
|
value = ''
|
||||||
|
opts = self.rel.to._meta
|
||||||
|
app_label = opts.app_label
|
||||||
|
model_name = opts.object_name.lower()
|
||||||
|
related_url = '../../../%s/%s/' % (app_label, model_name)
|
||||||
|
params = self.url_parameters()
|
||||||
|
if params:
|
||||||
|
url = '?' + '&'.join(['%s=%s' % (k, v) for k, v in params.items()])
|
||||||
|
else:
|
||||||
|
url = ''
|
||||||
|
if not attrs.has_key('class'):
|
||||||
|
attrs['class'] = 'vM2MRawIdAdminField'
|
||||||
|
# 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 = u''
|
||||||
|
context = {
|
||||||
|
'url': url,
|
||||||
|
'related_url': related_url,
|
||||||
|
'admin_media_prefix': settings.ADMIN_MEDIA_PREFIX,
|
||||||
|
'search_path': self.search_path,
|
||||||
|
'search_fields': ','.join(self.search_fields),
|
||||||
|
'model_name': model_name,
|
||||||
|
'app_label': app_label,
|
||||||
|
'label': label,
|
||||||
|
'name': name,
|
||||||
|
}
|
||||||
|
output.append(render_to_string(self.widget_template or (
|
||||||
|
'templates/widget/%s/%s/m2m_searchinput.html' % (app_label, model_name),
|
||||||
|
'templates/widget/%s/m2m_searchinput.html' % app_label,
|
||||||
|
'templates/widget/m2m_searchinput.html',
|
||||||
|
), context))
|
||||||
|
output.reverse()
|
||||||
|
return mark_safe(u''.join(output))
|
||||||
|
|
||||||
class RelatedFieldWidgetWrapper(forms.Widget):
|
class RelatedFieldWidgetWrapper(forms.Widget):
|
||||||
"""
|
"""
|
||||||
This class is a wrapper to a given widget to add the add icon for the
|
This class is a wrapper to a given widget to add the add icon for the
|
||||||
|
Loading…
x
Reference in New Issue
Block a user