# -*- coding: utf-8 -*- from django import forms from django.core.exceptions import ValidationError from django.db import models from django.utils.encoding import force_unicode from django.utils.text import capfirst class MultiSelectField(models.TextField): __metaclass__ = models.SubfieldBase def get_db_prep_value(self, value): if isinstance(value, basestring): return value elif isinstance(value, list): return ','.join(value) return '' def to_python(self, value): if isinstance(value, basestring): return value.split(',') elif isinstance(value, list): return value return '' def value_to_string(self, obj): # We need this to proper dump data. return self.get_db_prep_value(self._get_val_from_obj(obj)) def get_choices_default(self): return self.get_choices(include_blank=False) def formfield(self, form_class=forms.MultipleChoiceField, **kwargs): # Using super() won't work because this would replace the form_class. defaults = { 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text, 'choices': self.get_choices(include_blank=False), } if self.has_default(): if callable(self.default): defaults['initial'] = self.default defaults['show_hidden_initial'] = True else: defaults['initial'] = self.get_default() defaults.update(kwargs) return form_class(**defaults) def validate(self, value, model_instance): if isinstance(value, list): valid_choices = [k for k, v in self.choices] for choice in value: if choice not in valid_choices: raise ValidationError( self.error_messages['invalid_choice'] % choice) def _get_display(field): def _inner(self): values = getattr(self, field.attname) return ', '.join([force_unicode( field.choices_dict.get(value, value), strings_only=True ) for value in values]) return _inner def contribute_to_class(self, cls, name): self.set_attributes_from_name(name) self.model = cls cls._meta.add_field(self) self.choices_dict = dict(self.choices) setattr(cls, 'get_%s_display' % self.name, self._get_display())