Лучший виджет администратора ArrayField?

Есть ли способ сделать виджет администратора ArrayField возможным добавлять и удалять объекты? Кажется, что по умолчанию вместо него отображается только текстовое поле и используется разделение запятой для его значений.

Помимо неудобства, AFAICT в случае, когда базовое поле массива является Char/TextField, это не позволяет включать запятые в любой из текстов в массиве.

Ответ 2

Я не возражаю против этого (исходный код), но если вы используете PostgreSQL в качестве базы данных и готовы использовать реализацию ArrayField для Postgres, есть еще более простой вариант: подкласс ArrayField в модель и переопределить виджет администратора по умолчанию. Ниже приведена базовая реализация (протестирована в Django 1.9, 1.10, 1.11, 2.0, 2.1 и 2.2):

models.py

from django import forms
from django.db import models
from django.contrib.postgres.fields import ArrayField


class ChoiceArrayField(ArrayField):
    """
    A field that allows us to store an array of choices.
    Uses Django Postgres ArrayField
    and a MultipleChoiceField for its formfield.
    """

    def formfield(self, **kwargs):
        defaults = {
            'form_class': forms.MultipleChoiceField,
            'choices': self.base_field.choices,
        }
        defaults.update(kwargs)
        # Skip our parent formfield implementation completely as we don't
        # care for it.
        # pylint:disable=bad-super-call
        return super(ArrayField, self).formfield(**defaults)


FUNCTION_CHOICES = (
    ('0', 'Planning'),
    ('1', 'Operation'),
    ('2', 'Reporting'),
)


class FunctionModel(models.Model):
    name = models.CharField(max_length=128, unique=True)
    function = ChoiceArrayField(
        base_field=models.CharField(max_length=256, choices=FUNCTION_CHOICES),
        default=list)

Ответ 3

django-select2 предлагает способ отображения ArrayField с помощью Select2. В их документации приведен пример для ArrayField:

http://django-select2.readthedocs.io/en/latest/django_select2.html#django_select2.forms.Select2TagWidget

Чтобы отобразить уже выбранные значения:

class ArrayFieldWidget(Select2TagWidget):

    def render_options(self, *args, **kwargs):
        try:
            selected_choices, = args
        except ValueError:  # Signature contained `choices` prior to Django 1.10
            choices, selected_choices = args
        output = ['<option></option>' if not self.is_required and not self.allow_multiple_selected else '']
        selected_choices = {force_text(v) for v in selected_choices.split(',')}
        choices = {(v, v) for v in selected_choices}
        for option_value, option_label in choices:
            output.append(self.render_option(selected_choices, option_value, option_label))
        return '\n'.join(output)

    def value_from_datadict(self, data, files, name):
        values = super().value_from_datadict(data, files, name)
        return ",".join(values)

Чтобы добавить виджет в форму:

class MyForm(ModelForm):

    class Meta:
        fields = ['my_array_field']
        widgets = {
            'my_array_field': ArrayFieldWidget
        }