Djangoのformsに含まれるChoiceFieldやMultipleChoiceFieldだと、
choicesが必須なので、あらかじめ用意されたデータしか入力できない。
既存のデータではなく、タグやasinなどの文字列をWeb画面上で複数入力なり選択なりし、
それを文字列のリストとして処理できるフォームフィールドが欲しかったので作成した。
django.formsのMultipleChoiceFieldとCharFieldを参考にした。
max_lengthとmin_lengthは個々の文字列の長さをチェックする。
project/common/forms.py
from django import forms from django.utils.translation import ugettext_lazy as _ from django.utils.encoding import smart_unicode from django.forms.widgets import MultipleHiddenInput from django.forms.util import ValidationError class MultipleCharField(forms.Field): widget = MultipleHiddenInput hidden_widget = MultipleHiddenInput default_error_messages = { 'max_length': _(u'Ensure this value has at most %(max)d characters (it has %(length)d).'), 'min_length': _(u'Ensure this value has at least %(min)d characters (it has %(length)d).'), } def __init__(self, max_length=None, min_length=None, *args, **kwargs): self.max_length, self.min_length = max_length, min_length super(MultipleCharField, self).__init__(*args, **kwargs) def clean(self, value): if self.required and not value: raise ValidationError(self.error_messages['required']) elif not self.required and not value: return [] if not isinstance(value, (list, tuple)): raise ValidationError(self.error_messages['invalid_list']) new_value = [smart_unicode(val) for val in value] for val in new_value: value_length = len(val) if self.max_length is not None and value_length > self.max_length: raise ValidationError(self.error_messages['max_length'] % {'max': self.max_length, 'length': value_length}) if self.min_length is not None and value_length < self.min_length: raise ValidationError(self.error_messages['min_length'] % {'min': self.min_length, 'length': value_length}) return new_value
フォームはこんな感じで定義
project/app/forms.py
from django import forms from project.common.forms import MultipleCharField class MultiFieldTestForm(forms.ModelForm): contents = forms.CharField(required=True) tags = MultipleCharField(required=False, max_length=100)
HTML上では、リストの要素分だけhiddenタグが出力されるので、
Javascriptで追加したり削除したりする。
templates/form.html
... <form> コメント:{{ form.contents }} <!-- 以下にhiddenタグが出力される --> {{ form.tags }} </form> ...
フォームデータの取得する時はcleaned_dataでリストを取得できる。
project/app/views.py
... def form_complete(request): form = MultiFieldTestForm(request.POST) if form.is_valid(): form_data = form.cleaned_data tag_list = form_data["tags"] # 各hiddenタグの内容を要素として持つリストが返る ...