【问题标题】:django admin override filter_horizontaldjango admin 覆盖 filter_horizo​​ntal
【发布时间】:2019-08-15 03:23:16
【问题描述】:

我的目标是制作一个“高级”filter_horizo​​​​ntal,一个具有更多过滤器的过滤器,但我似乎无法找到要覆盖的小部件。 我知道它使用了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_horizo​​ntal.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 的左侧)

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'))`

在编辑现有字段时,我无法找到在“to”部分中预填充值的位置。

【问题讨论】:

    标签: django django-admin django-admin-filters


    【解决方案1】:

    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 或媒体覆盖

    您可以观察到FilteredSelectMultiple 类上的media 属性包含多个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 类中覆盖这些选项。

    希望以上信息对您有用。

    【讨论】:

    • 谢谢,这就是我最终选择的。
    • 我准备了一个名为 AjaxFitlerHorizo​​ntal 的自定义小部件,只是有几个问题:在哪里将查询集定义为 init () 上的空以及我们编辑表单时要设置的值?
    • 能否将AjaxFitlerHorizontal附上您的问题或单独的问题?
    • 非常感谢您的帮助,我将代码添加到问题中,如果我采取的方法是正确的,我不知道,因为它主要是 javascript 方面。
    猜你喜欢
    • 2015-09-08
    • 2011-07-17
    • 1970-01-01
    • 2013-08-12
    • 2012-01-31
    • 2012-04-24
    • 2019-12-05
    • 1970-01-01
    相关资源
    最近更新 更多