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
Comments