Django管理员覆盖filter_horizo​​ntal [英] django admin override filter_horizontal

查看:46
本文介绍了Django管理员覆盖filter_horizo​​ntal的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的目标是制作一个具有更多过滤器的高级" filter_horizo​​ntal,但是我似乎找不到要覆盖的小部件.我知道它使用related_widget_wrapper.html,但是如果我想以明确的方式向其添加功能,则要覆盖的小部件是什么.

I'm aiming to make an "advanced" filter_horizontal, one with more filters, but I can't seem to find the widget to override. I know it uses the related_widget_wrapper.html, but if I want to add functionalities to it in a clear way, what is the widget to override.

现在,我的备份解决方案是做一个完整的javascript解决方案,以在表单加载(从javascript创建)中添加一个下拉列表,并进行ajax调用来修改过滤器...但这似乎是一个过大的杀伤力.

For now my backup solution is to do a full javascript solution to prepend it with a dropdown on form load (created from javascript) and make ajax calls to modify the filter...but this seems as an overkill.

我到目前为止所做的:

# 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_horizo​​ntal.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_horizo​​ntal的左侧)进行了

I had to override the field, just to remove validation(for some reason the validation is also done on the original values, the left side of the 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'))`

在编辑现有字段时,我无法在至"部分中找到预填充值的位置.

I can't manage to find where to prefill the values in the "to" section when I'm editing an existing field.

推荐答案

Django Model admin覆盖包含以下代码的 BaseModelAdmin .

Django Model admin overrides BaseModelAdmin which contains following code.

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_horizo​​ntal 参数在 ModelAdmin 选项中添加了 FilteredSelectMultiple 小部件.

It can be observed that if there is filter_vertical or filter_horizontal argument passed in ModelAdmin option than it adds FilteredSelectMultiple widget.

下面是 FilteredSelectMultiple 的来源.如有必要,您可以覆盖此

Below is the source of FilteredSelectMultiple You may override this if necessary

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或媒体替代

您可能会发现 FilteredSelectMultiple 类的 media 属性包含多个js,您可以根据需要对其进行修改.

You could observe that media property on FilteredSelectMultiple class have several js included you may modify them as per your needs.

用于修改HTML模板

FilteredSelectMultiple 覆盖 django.forms.widgets.SelectMultiple ,最终覆盖 django.forms.widgets.Select 小部件.

FilteredSelectMultiple overrides django.forms.widgets.SelectMultiple which ultimately overrides django.forms.widgets.Select widget.

因此可以说 FilteredSelectMultiple 使用了 Select 小部件

So it can be said that FilteredSelectMultiple uses following properties of Select widget

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 类中覆盖这些选项.

You can override those options inside your FilteredSelectMultiple class.

我希望以上信息对您有用.

I hope the information above is useful for you.

这篇关于Django管理员覆盖filter_horizo​​ntal的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆