Django 管理员自定义 ArrayField 小部件 [英] Django admin custom ArrayField widget

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

问题描述

ArrayField 的当前管理小部件是一个字段,以逗号作为分隔符,如下所示(文本列表):

这并不理想,因为我会有更长的文本(甚至 20 个字)并包含逗号.我可以

我可以使用另一个表来解决这个问题,但我想知道是否可以这样解决.

解决方案

不幸的是,Django 还没有为 ArrayField 提供方便的小部件.我建议你创建自己的.以下是 Django>=1.11 的示例:

类 DynamicArrayWidget(forms.TextInput):template_name = 'myapp/forms/widgets/dynamic_array.html'def get_context(self, name, value, attrs):值 = 值或 ['']context = super().get_context(name, value, attrs)final_attrs = context['widget']['attrs']id_ = context['widget']['attrs'].get('id')子小部件 = []对于索引,枚举中的项目(上下文 ['widget']['value']):widget_attrs = final_attrs.copy()如果 id_:widget_attrs['id'] = '%s_%s' % (id_, index)小部件 = forms.TextInput()widget.is_required = self.is_requiredsubwidgets.append(widget.get_context(name, item, widget_attrs)['widget'])上下文['widget']['subwidgets'] = subwidgets返回上下文def value_from_datadict(自我,数据,文件,名称):尝试:getter = data.getlist除了属性错误:getter = data.get返回 getter(名称)def format_value(自我,价值):返回值或 []

这是小部件模板:

{% 无空格 %}<div class="动态数组小部件"><ul>{% for widget.subwidgets %}<li class="array-item">{% include widget.template_name %}</li>{% endfor %}</ul><div><button type="button" class="add-array-item">添加另一个</button></div></div>{% endspaceless %}

一些javascript(为了方便使用jQuery):

$('.dynamic-array-widget').each(function() {$(this).find('.add-array-item').click((function($last) {返回函数(){var $new = $last.clone()var id_parts = $new.find('input').attr('id').split('_');var id = id_parts.slice(0, -1).join('_') + '_' + String(parseInt(id_parts.slice(-1)[0]) + 1)$new.find('input').attr('id', id);$new.find('input').prop('value', '');$new.insertAfter($last);};})($(this).find('.array-item').last()));});

您还必须创建自己的表单域:

from itertools 导入链从 django 导入表格从 django.contrib.postgres.utils 导入 prefix_validation_error类 DynamicArrayField(forms.Field):default_error_messages = {'item_invalid': '数组中的项目 %(nth)s 未验证:',}def __init__(self, base_field, **kwargs):self.base_field = base_fieldself.max_length = kwargs.pop('max_length', 无)kwargs.setdefault('widget', DynamicArrayWidget)super().__init__(**kwargs)def 清洁(自我,价值):清理数据 = []错误= []值 = 过滤器(无,值)对于索引,枚举(值)中的项目:尝试:clean_data.append(self.base_field.clean(item))除了 forms.ValidationError 作为错误:errors.append(prefix_validation_error(错误,self.error_messages['item_invalid'],代码='item_invalid',参数={'nth':索引},))如果错误:提高 forms.ValidationError(list(chain.from_iterable(errors)))如果cleaned_data 和self.required:提高 forms.ValidationError(self.error_messages['required'])返回cleaned_data

最后,在表单上明确设置:

类 MyModelForm(forms.ModelForm):元类:模型 = 我的模型字段 = ['foo', 'bar', 'the_array_field']field_classes = {'the_array_field': DynamicArrayField,}

The current admin widget for ArrayField is one field, with comma as delimiter, like this (text list):

This isn't ideal because I would have longer texts (even 20 words) and contain commas. I could change the delimiter to be something else but that still doesn't help with unreadable content in admin.

What I would like is having a list of fields, that I can alter in admin. Something similar to the following image

I could use another table to solve this, but I wonder if it's possible to solve it this way.

解决方案

Unfortunately Django does not ship with a convenient widget for ArrayFields yet. I'd suggest you to create your own. Here is an example for Django>=1.11:

class DynamicArrayWidget(forms.TextInput):

    template_name = 'myapp/forms/widgets/dynamic_array.html'

    def get_context(self, name, value, attrs):
        value = value or ['']
        context = super().get_context(name, value, attrs)
        final_attrs = context['widget']['attrs']
        id_ = context['widget']['attrs'].get('id')

        subwidgets = []
        for index, item in enumerate(context['widget']['value']):
            widget_attrs = final_attrs.copy()
            if id_:
                widget_attrs['id'] = '%s_%s' % (id_, index)
            widget = forms.TextInput()
            widget.is_required = self.is_required
            subwidgets.append(widget.get_context(name, item, widget_attrs)['widget'])

        context['widget']['subwidgets'] = subwidgets
        return context

    def value_from_datadict(self, data, files, name):
        try:
            getter = data.getlist
        except AttributeError:
            getter = data.get
        return getter(name)

    def format_value(self, value):
        return value or []

Here is the widget template:

{% spaceless %}
<div class="dynamic-array-widget">
  <ul>
    {% for widget in widget.subwidgets %}
      <li class="array-item">{% include widget.template_name %}</li>
    {% endfor %}
  </ul>
  <div><button type="button" class="add-array-item">Add another</button></div>
</div>
{% endspaceless %}

A few javascript (using jQuery for convenience):

$('.dynamic-array-widget').each(function() {
    $(this).find('.add-array-item').click((function($last) {
        return function() {
            var $new = $last.clone()
            var id_parts = $new.find('input').attr('id').split('_');
            var id = id_parts.slice(0, -1).join('_') + '_' + String(parseInt(id_parts.slice(-1)[0]) + 1)
            $new.find('input').attr('id', id);
            $new.find('input').prop('value', '');
            $new.insertAfter($last);
        };
    })($(this).find('.array-item').last()));
});

And you would also have to create your own form field:

from itertools import chain

from django import forms
from django.contrib.postgres.utils import prefix_validation_error

class DynamicArrayField(forms.Field):

    default_error_messages = {
        'item_invalid': 'Item %(nth)s in the array did not validate: ',
    }

    def __init__(self, base_field, **kwargs):
        self.base_field = base_field
        self.max_length = kwargs.pop('max_length', None)
        kwargs.setdefault('widget', DynamicArrayWidget)
        super().__init__(**kwargs)

    def clean(self, value):
        cleaned_data = []
        errors = []
        value = filter(None, value)
        for index, item in enumerate(value):
            try:
                cleaned_data.append(self.base_field.clean(item))
            except forms.ValidationError as error:
                errors.append(prefix_validation_error(
                    error, self.error_messages['item_invalid'],
                    code='item_invalid', params={'nth': index},
                ))
        if errors:
            raise forms.ValidationError(list(chain.from_iterable(errors)))
        if cleaned_data and self.required:
            raise forms.ValidationError(self.error_messages['required'])
        return cleaned_data

Finally, set it explicitly on your forms:

class MyModelForm(forms.ModelForm):

    class Meta:
        model = MyModel
        fields = ['foo', 'bar', 'the_array_field']
        field_classes = {
            'the_array_field': DynamicArrayField,
        }

这篇关于Django 管理员自定义 ArrayField 小部件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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