Django admin custom ArrayField widget Django admin custom ArrayField widget django django

Django admin custom ArrayField widget


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 chainfrom django import formsfrom django.contrib.postgres.utils import prefix_validation_errorclass 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,        }


Try to take a look in this one :

Better ArrayField admin widget?

I think is more about a js thing after you have rendered the Array in a different way.