from django import forms from django.db import models from django.utils.text import capfirst """ CharField based Tag field. """ class TagField(models.CharField): __metaclass__ = models.SubfieldBase def __init__(self, choices, separator=None, *args, **kwds): self.separator = separator or '\t' kwds['choices'] = choices kwds['max_length'] = sum((len(c[0]) for c in choices)) + len(choices) super(TagField, self).__init__(*args, **kwds) def to_python(self, value): if isinstance(value, unicode): tags = value.split(self.separator) return [{'title':c[1], 'slug': c[0]} for c in self.choices if c[0] in tags] return value def get_db_prep_save(self, value): data = [] slug_list = [] title_list = [] try: slug_list = [v['slug'] for v in value] except TypeError: title_list = value for slug, title in self.choices: if slug_list and slug in slug_list \ or title in title_list: data.append(slug) continue return self.separator.join(data) def formfield(self, form_class=None, **kwargs): defaults = { 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text } form_class = form_class or TagFormField defaults['choices'] = self.get_choices(include_blank=False) defaults['coerce'] = self.to_python for k in kwargs.keys(): if k not in ('choices', 'label', 'help_text', 'error_messages'): del kwargs[k] defaults.update(kwargs) return form_class(**defaults) class TagCheckboxSelect(forms.widgets.CheckboxSelectMultiple): def __init__(self, *args, **kwds): super(TagCheckboxSelect, self).__init__(*args, **kwds) def render(self, name, value, attrs=None, choices=()): data = [] if value: data = [v['slug'] for v in value] return super(TagCheckboxSelect, self).render(name, data, attrs, choices) class TagFormField(forms.ChoiceField): widget = TagCheckboxSelect field = TagField def __init__(self, *args, **kwds): kwds.pop('coerce', None) super(TagFormField, self).__init__(*args, **kwds) def clean(self, value): if not value: raise forms.ValidationError('Choose at least one tag') data = [] for choice in self.choices: if choice[0] in value: data.append(choice[1]) continue return data