ほんじゃらねっと

ダイエット中プログラマのブログ

文字列のリストを扱うカスタムフォームフィールドを作成する

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タグの内容を要素として持つリストが返る
...