Django admin custom ArrayField widget
Unfortunately Django does not ship with a convenient widget for ArrayField
s 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.