OK thanks for the replies. The first enhancement would be a change in how choices are handled for the ArrayField. With this change,
choices would be based on the base field. If choices keyword argument is given, a multiple choice field is used and the array value will be composed
of these choices. I think this would make the choices keyword argument more useful for ArrayField as currently you would have to choose between a list of arrays.
The changes would need to be made to the validate and formfield methods:
def validate(self, value, model_instance):
"""
Validates value and throws ValidationError. Subclasses should override
this to provide validation logic.
Choices are handled differently than other fields. Choices must be based on the BaseField
and thus multiple choices can be given.
"""
if not self.editable:
# Skip validation for non-editable fields.
return
if self.choices and value not in self.empty_values:
if isinstance(value, (list, tuple)):
option_keys = [x[0] for x in self.choices]
if all(x in option_keys for x in value):
return
else:
for option_key, option_value in self.choices:
if isinstance(option_value, (list, tuple)):
# This is an optgroup, so look inside the group for
# options.
for optgroup_key, optgroup_value in option_value:
if value == optgroup_key:
return
elif value == option_key:
return
raise exceptions.ValidationError(
self.error_messages['invalid_choice'],
code='invalid_choice',
params={'value': value},
)
if value is None and not self.null:
raise exceptions.ValidationError(self.error_messages['null'], code='null')
if not self.blank and value in self.empty_values:
raise exceptions.ValidationError(self.error_messages['blank'], code='blank')
for index, part in enumerate(value):
try:
self.base_field.validate(part, model_instance)
except exceptions.ValidationError as error:
raise prefix_validation_error(
error,
prefix=self.error_messages['item_invalid'],
code='item_invalid',
params={'nth': index},
)
if isinstance(self.base_field, ArrayField):
if len({len(i) for i in value}) > 1:
raise exceptions.ValidationError(
self.error_messages['nested_array_mismatch'],
code='nested_array_mismatch',
)
def formfield(self, **kwargs):
defaults = {
'form_class': SimpleArrayField,
'base_field': self.base_field.formfield(),
'choices_form_class': TypedMultipleChoiceField,
'max_length': self.size,
}
if self.choices:
defaults['coerce'] = self.base_field.to_python
defaults.update(kwargs)
return super(ArrayField, self).formfield(**defaults)
Sample Usage:
class User(model.Model):
Roles = ArrayField(base_field=IntegerField(), choices=((0, 'Can Read'), (1,' Can Edit', ), (2, 'Can Create'), (3, 'Can_Delete')))