Skip to content

Instantly share code, notes, and snippets.

@mattmcc
Created August 28, 2013 00:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mattmcc/6360641 to your computer and use it in GitHub Desktop.
Save mattmcc/6360641 to your computer and use it in GitHub Desktop.
class ChoiceList(list):
def __init__(self, *args, **kwargs):
self.choices = kwargs.pop('choices')
super(ChoiceList, self).__init__(*args, **kwargs)
def items(self):
return [(k, v) for k, v in self.choices if k in self]
class ChoiceArrayField(ArrayField):
def __init__(self, *args, **kwargs):
#self.choices = kwargs.pop('choices')
super(ChoiceArrayField, self).__init__(self, *args, **kwargs)
# Copied from Django 1.6, which fixes #18162, and adjusted to
# support array fields
def formfield(self, form_class=None, **kwargs):
"""
Returns a django.forms.Field instance for this database Field.
"""
defaults = {'required': not self.blank,
'label': capfirst(self.verbose_name),
'help_text': self.help_text}
if self.has_default():
if callable(self.default):
defaults['initial'] = self.default
defaults['show_hidden_initial'] = True
else:
defaults['initial'] = self.get_default()
if self.choices:
defaults['choices'] = self.get_choices(include_blank=False)
defaults['coerce'] = lambda val: val
if form_class is None or not issubclass(form_class, forms.TypedMultipleChoiceField):
form_class = forms.TypedMultipleChoiceField
# Many of the subclass-specific formfield arguments (min_value,
# max_value) don't apply for choice fields, so be sure to only pass
# the values that TypedChoiceField will understand.
for k in list(kwargs):
if k not in ('coerce', 'empty_value', 'choices', 'required',
'widget', 'label', 'initial', 'help_text',
'error_messages', 'show_hidden_initial'):
del kwargs[k]
defaults.update(kwargs)
if form_class is None:
form_class = forms.CharField
return form_class(**defaults)
# Copied from django.db.models.fields.Field and adjusted to support
# multiple values.
def validate(self, value, model_instance):
"""
Validates value and throws ValidationError. Subclasses should override
this to provide validation logic.
"""
if not self.editable:
# Skip validation for non-editable fields.
return
if self._choices and value:
if isinstance(value, (list, tuple)):
value_set = set(value)
else:
value_set = set((value,))
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.
optgroup_keys = [key for key, value in option_value]
for optgroup_key, optgroup_value in option_value:
if optgroup_key in value_set:
value_set.remove(optgroup)
elif option_key in value_set:
value_set.remove(option_key)
if value_set:
msg = self.error_messages['invalid_choice'] % value
raise exceptions.ValidationError(msg)
return
if value is None and not self.null:
raise exceptions.ValidationError(self.error_messages['null'])
if not self.blank and value in validators.EMPTY_VALUES:
raise exceptions.ValidationError(self.error_messages['blank'])
def to_python(self, value):
if value is None:
value = []
return ChoiceList(value, choices=self.choices)
add_introspection_rules([
(
[ChoiceArrayField],
[],
{'dbtype': ["_array_type", {"default" : "int"}]},
),
], ['someapp\.fields\.ChoiceArrayField']
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment