我的目标是制作一个具有更多过滤器的“高级” filter_horizontal,但是我似乎找不到要覆盖的小部件。 我知道它使用related_widget_wrapper.html,但是如果我想以明确的方式向其添加功能,则要覆盖的小部件是什么。
现在,我的备份解决方案是做一个完整的javascript解决方案,以在表单负载(从javascript创建)中添加一个下拉列表,并进行ajax调用来修改过滤器...但这似乎是一个过大的选择。
我到目前为止所做的:
# Override filteredSelectMultiple, add javascript and add attributes on the tag to identify the element, and add parameter url that will contain the ajax call
class AjaxFilterHorizontalWidget(FilteredSelectMultiple):
def __init__(self, url, verbose_name = '', is_stacked=False, attrs=None, choices=()):
self.url = url
super().__init__(verbose_name, is_stacked, attrs, choices)
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
context['widget']['attrs']['data-url'] = self.url
context['widget']['attrs']['data-ajax-select'] = '1'
return context
class Media:
js = ['admin/js/ajax_filter_horizontal.js']
Ajax_filter_horizontal.js
$(document).ready(function () {
$('select[data-ajax-select=1]').each(function (index, item) {
var currentRequest;
var url = $(item).data('url')
// var wrapper = $('#' + $(item).prop('id')).closest('.selector-available')
$(document).on('keyup', $('.selector-filter input'), function () {
if ($('.selector-filter input').val().length < 3) {
$(item).empty()
return
}
currentRequest = $.ajax({
url: url,
data: {q: $('.selector-filter input').val()},
beforeSend : function() {
if(currentRequest != null) {
currentRequest.abort();
}
},
success: function (data) {
$(item).empty()
let item_to = $('#' + $(item).prop('id').replace('_from', '_to'))
if (data.results.length > 500) {
$('#' + $(item).prop('id')).append('<option disabled value="" title="">Too many results, refine your search...</option>')
return
}
for (let instance of data.results) {
if ($('option[value='+instance.id+']', item_to).length == 0) {
$('#' + $(item).prop('id')).append('<option value="'+instance.id+'" title="'+instance.text+'">'+instance.text+'</option>')
}
}
SelectBox.init($(item).prop('id'))
}
})
});
});
});
我不得不重写该字段,只是为了删除验证(出于某种原因,验证也对原始值(filter_horizontal的左侧)进行了
class AjaxMultipleChoiceField(MultipleChoiceField):
widget = AjaxFilterHorizontalWidget
def validate(self, value):
pass
"""Validate that the input is a list or tuple."""
# if self.required and not value:
# raise ValidationError(self.error_messages['required'], code='required')
这就是我所说的:
self.fields['person'] = `AjaxMultipleChoiceField(widget=AjaxFilterHorizontalWidget(url= '/person-autocomplete-advanced/', verbose_name='People to invite'))`
在编辑现有字段时,我无法在“至”部分中找到预填值的位置。
答案 0 :(得分:2)
Django模型管理员会覆盖BaseModelAdmin
,其中包含以下代码。
django.contrib.admin.options.py
class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
...
def formfield_for_dbfield(self, db_field, request, **kwargs):
...
if db_field.name in self.raw_id_fields:
kwargs['widget'] = widgets.ManyToManyRawIdWidget(db_field.remote_field, self.admin_site, using=db)
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
)
可以观察到是否传递了filter_vertical
或filter_horizontal
参数
在ModelAdmin
选项中添加了FilteredSelectMultiple
小部件。
以下是FilteredSelectMultiple
的来源
django.contrib.admin.widgets.py
class FilteredSelectMultiple(forms.SelectMultiple):
"""
A SelectMultiple with a JavaScript filter interface.
Note that the resulting JavaScript assumes that the jsi18n
catalog has been loaded in the page
"""
@property
def media(self): # override this property in your custom class
js = ["core.js", "SelectBox.js", "SelectFilter2.js"]
return forms.Media(js=["admin/js/%s" % path for path in js])
...
def get_context(self, name, value, attrs):
context = super(FilteredSelectMultiple, self).get_context(name, value, attrs)
context['widget']['attrs']['class'] = 'selectfilter'
if self.is_stacked:
context['widget']['attrs']['class'] += 'stacked'
context['widget']['attrs']['data-field-name'] = self.verbose_name
context['widget']['attrs']['data-is-stacked'] = int(self.is_stacked)
return context
用于JS或媒体替代
您可能会发现media
类的FilteredSelectMultiple
属性包含多个js,您可以根据需要对其进行修改。
用于修改HTML模板
FilteredSelectMultiple
会覆盖django.forms.widgets.SelectMultiple
,最终会覆盖django.forms.widgets.Select
小部件。
因此可以说FilteredSelectMultiple
使用了Select
小部件的以下属性
class Select(ChoiceWidget):
input_type = 'select'
template_name = 'django/forms/widgets/select.html'
option_template_name = 'django/forms/widgets/select_option.html'
add_id_index = False
checked_attribute = {'selected': True}
option_inherits_attrs = False
...
您可以在FilteredSelectMultiple
类中覆盖这些选项。
我希望以上信息对您有用。