본글 : https://naruport.com/blog/2019/7/25/django-custom-form/
같이보면 좋은 자료 : https://developer.mozilla.org/ko/docs/Learn/Server-side/Django/Forms



煉獄!Djangoのフォームを俺好みに改造する

65, 2019-07-25
Djangoロゴ

目次

はじめに

以下のようなフォームがある。

改造前のフォーム

# forms from django import forms class ProfileForm(forms.Form): name = forms.CharField() age = forms.IntegerField()

# templates <form action="{% url 'index' %}" method="POST"> {% csrf_token %} {{ profile_form }} <input type="submit" value="送信"> </form>

# view

from django.http import HttpResponseRedirect from django.shortcuts import render from .forms import NameForm def get_name(request): # if this is a POST request we need to process the form data if request.method == 'POST': # create a form instance and populate it with data from the request: form = NameForm(request.POST) # check whether it's valid: if form.is_valid(): # process the data in form.cleaned_data as required # ... # redirect to a new URL: return HttpResponseRedirect('/thanks/') # if a GET (or any other method) we'll create a blank form else: form = NameForm() return render(request, 'name.html', {'profile_form ': form})



これを改造していく。

Formの多様なフォーマット
 BooleanFieldCharFieldChoiceFieldTypedChoiceFieldDateFieldDateTimeFieldDecimalFieldDurationFieldEmailFieldFileFieldFilePathFieldFloatFieldImageFieldIntegerFieldGenericIPAddressFieldMultipleChoiceFieldTypedMultipleChoiceFieldNullBooleanFieldRegexFieldSlugFieldTimeFieldURLFieldUUIDFieldComboFieldMultiValueFieldSplitDateTimeFieldModelMultipleChoiceFieldModelChoiceField .
(
https://developer.mozilla.org/ko/docs/Learn/Server-side/Django/Forms)

フィールドを参照する

テンプレート内でフォームのフィールド名を参照すると対応するフィールドが取り出せる。

from django import forms


class ProfileForm(forms.Form):
    name = forms.CharField()
    age = forms.IntegerField()
{% with profile_form as f %}
<form action="{% url 'index' %}" method="POST">
    {% csrf_token %}
    <div>{{ f.name }}</div>
    <div>{{ f.age }}</div>
    <input type="submit" value="送信">
</form>
{% endwith %}

しかしインプット要素のみの取り出しなので味気がない出力になる。

ラベルを参照する

ラベル名を参照するにはフィールドのlabelを参照する。ちなみにlabel名の参照ではHTMLlabelは生成されないので、別途タグで囲む必要がある。

{% with profile_form as f %}
<form action="{% url 'index' %}" method="POST">
    {% csrf_token %}
    <div><label>{{ f.name.label }}</label>: {{ f.name }}</div>
    <div><label>{{ f.age.label }}</label>: {{ f.age }}</div>
    <input type="submit" value="送信">
</form>
{% endwith %}

ラベル名を変更するにはフォームのフィールドのlabelを指定する。

from django import forms


class ProfileForm(forms.Form):
    name = forms.CharField(label='名前')
    age = forms.IntegerField(label='年齢')


대부분의 필드에 공통적인 인자들은 아래와 같다. ( 이들은 적절한 기본값을 가지고 있다 ):

  • requiredTrue 로 설정되면, 필드를 빈칸으로 두거나 None 값을 줄 수 없게된다. 보통필드는 required는 True로 기본 설정되므로, 폼에서 빈 칸을 허용하기 위해서는required=False 로 설정해야 한다. 
  • label: HTML에서 필드를 렌더링할때 사용하는 레이블이다. label 이 지정되지 않으면,  Django는 필드 이름에서 첫번째 문자를 대문자로, 밑줄을 공백으로 변형한 레이블을 새로 생성할 것이다. (예를 들면, renewal_date  --> Renewal date).
  • label_suffix: 기본적으로, 콜론(:)이 레이블 다음에 표시된다. (예를 들면, Renewal date:). 이 인자는 다른 문자(들)를 포함한 접미사를 지정할 수 있도록 해준다.
  • initial: 폼이 나타날 때 해당 필드의 초기 값.
  • widget: 사용할 디스플레이 위젯.
  • help_text (위의 예에서 봤듯이): 필드 사용법을 보여주는 추가적인 문구.
  • error_messages: 해당 필드의 에러 메시지 목록. 필요하면 문구를 수정할 수 있다.
  • validators: 해당 필드가 유효한 값을 가질 때 호출되는 함수의 목록.
  • localize: 폼 데이타 입력의 현지화(localisation)를 허용함 (자세한 정보는 해당 링크 참조).
  • disabled: 이 옵션이 True 일때 해당 필드를 볼 수는 있지만 편집이 안됨. 기본 값은 False.




出力結果。

ラベルを付けたフォーム

ヘルプの参照

フォームのフィールドにhelp_textを設定した場合、テンプレートではフィールドのhelp_textから設定文字列を参照できる。

from django import forms


class ProfileForm(forms.Form):
    name = forms.CharField(label='名前', help_text='英字のみ')
    age = forms.IntegerField(label='年齢', help_text='0以上120以下')
{% with profile_form as f %}
<form action="{% url 'index' %}" method="POST">
    {% csrf_token %}
    <div>
        <label>{{ f.name.label }}</label>:
        {{ f.name }}
        <p>{{ f.name.help_text }}</p>
    </div>
    <div>
        <label>{{ f.age.label }}</label>:
        {{ f.age }}
        <p>{{ f.age.help_text }}</p>
    </div>
    <input type="submit" value="送信">
</form>
{% endwith %}

出力結果。

ヘルプを付けたフォーム

エラーの参照

フォームは検証に失敗するとエラー文字列をフォームに格納する。

{% with profile_form as f %}
<form action="{% url 'index' %}" method="POST">
    {% csrf_token %}

    {# フィールドに属さない全体的なエラーの参照 #}
    {% if f.errors %}
        <p>{{ f.non_field_errors }}</p>
    {% endif %}

    <div>
        <label>{{ f.name.label }}</label>:
        {{ f.name }}
        <p>{{ f.name.help_text }}</p>
        {# フィールドに属しているエラーの参照 #}
        <p>{{ f.name.errors }}</p>
    </div>
    <div>
        <label>{{ f.age.label }}</label>:
        {{ f.age }}
        <p>{{ f.age.help_text }}</p>
        {# フィールドに属しているエラーの参照 #}
        <p>{{ f.age.errors }}</p>
    </div>
    <input type="submit" value="送信">
</form>
{% endwith %}

バリデーションの追加

バリデーションを追加するにはフォームにclean_で始まるメソッドを定義する(参照: http://python.keicode.com/django/form-validation.php)。
デフォルトのバリデーターが呼ばれた後に実行されるらしい。

from django import forms class ProfileForm(forms.Form): name = forms.CharField(max_length=20, label='名前', help_text='英字のみ') age = forms.IntegerField(label='年齢', help_text='0以上120以下') def clean_name(self): # viewのform.is_validが実行されるとdef clean_xxxx系の関数から有効性検査を行う name = self.cleaned_data['name'] isallalpha = True for c in name: if not c.isalpha(): isallalpha = False break if not isallalpha: raise forms.ValidationError('英字以外が含まれています。') return name def clean_age(self): """ ちなみにフィールドの属性 min_value, max_valueで入力範囲を制限できる。 それらを設定したらこのバリデーションは不要? """ age = self.cleaned_data['age'] if age < 0 or age > 120: raise forms.ValidationError('範囲外です。') return age

フォームに不正な値を入力してみた出力結果。

エラー表示を付けたフォーム

ちなみにテンプレートでerrorsを参照したい場合、views.pyでちょっとした工夫が必要になる。is_validを呼んだ後のフォームを再度出力用のテンプレートに渡すとerrorsを参照できるようになる。

from django.shortcuts import render
from django.http import HttpResponse
from . import forms


def index(req):
    if req.method == 'GET':
        return render(req, 'myapp/profile-form.html', {
            'profile_form': forms.ProfileForm(),
        })
    elif req.method == 'POST':
        profile_form = forms.ProfileForm(req.POST)
        if not profile_form.is_valid():
            # ここでerrorsを生かしたままテンプレートに渡す。
            return render(req, 'myapp/profile-form.html', {
                'profile_form': profile_form,
            })

        return HttpResponse('ok')

プレースホルダーの設定

インプット要素のプレースホルダーはフォームのwidgetで設定できる。widgetの種類は https://github.com/django/django/blob/master/django/forms/widgets.py を参照。

class ProfileForm(forms.Form):
    name = forms.CharField(max_length=20, label='名前', help_text='英字のみ', widget=forms.TextInput(attrs={
        'placeholder': 'Name',
    }))
    age = forms.IntegerField(min_value=0, max_value=120, label='年齢', help_text='0以上120以下', widget=forms.NumberInput(attrs={
        'placeholder': '0',
    }))

出力結果。

プレースフォルダーを付けたフォーム

だいぶフォームらしくなりました(・∀・)ノ


+ Recent posts