8
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

DjangoのFormクラスを使う

Last updated at Posted at 2019-04-21

背景

DjangoのFormクラス、あるいはFlaskのWTFormsは柔軟な故使いこなすのに苦労する割に情報も少ない。

そこでこちらの記事では実際に使ってみながら機能を確認していく。

今回はDjangoのFormクラスを利用する。

フォームを文字列で出力してみる

実装

form.py
from django import forms


class ContactForm(forms.Form):
    subject = forms.CharField()
views.py
def index(request):
    return HttpResponse(str(ContactForm()))

表示

image.png

出力されるのはinput fieldのみでform要素やsubmit要素などは出力されない

<input type="text" name="subject" required="" id="id_subject">

内部実装

BaseForm::__str__ を経て BaseForm::as_table (HTML table要素としてフォームを出力するメソッド)を呼び出している

フォームインスタンスに格納されたデータを見てみる

実装

form.py
from django import forms


class ContactForm(forms.Form):
    subject = forms.CharField()
    age = forms.IntegerField()
views.py
def index(request):
    form = ContactForm()
    params={
        'form': form
    }
    return render(request, 'index.html', params)
index.html
<div>
  {{form.data}}
</div>

<div>
  {{form.fields}}
</div>

表示

{}
OrderedDict([('subject', <django.forms.fields.CharField object at 0x104ab1f98>), ('age', <django.forms.fields.IntegerField object at 0x104aca080>)])

内部実装

  • dataは初期化時に引数を見て、無ければ空のdictが入る
  • fieldsは初期化時にOrderedDictとして作られる。引数で並び替えをすることもできる

データを初期化して色々出してみる

実装

views.py
data = {'subject': 'hello',
        'age': 21}

def index(request):
    form = ContactForm(data)
    params={
        'form': form
    }
    return render(request, 'index.html', params)
index.html
<div>
  {{form.data}}
</div>

<div>
  {{form.fields}}
</div>

<div>
  {{form}}
</div>

<div>
  {{form.subject}}
</div>

<div>
  {{form.age}}
</div>

出力

image.png

メモ

インタラクティブシェルでの動作

>>> form.fields['age']
<django.forms.fields.IntegerField object at 0x10d906e10>
>>> str(form.fields['age'])
'<django.forms.fields.IntegerField object at 0x10d906e10>'
>>> form['age']
<django.forms.boundfield.BoundField object at 0x10d906fd0>
>>> str(form['age'])
'<input type="number" name="age" value="21" required id="id_age">'

boundfieldは
https://docs.djangoproject.com/en/2.2/ref/forms/api/#more-granular-output

POSTできるようにする

ドキュメントより

以下のようにformタグとsubmit要素は自前で実装する

<form action="/your-name/" method="post">
    {% csrf_token %}
    {{ form }}
    <input type="submit" value="Submit">
</form>

viewsの実装

Djangoの場合Viewクラスを使っていい感じに書くこともできるがミニマルに実装するならこんな風に出し分ける

def index(request):
  if request.method == 'POST':
    form = ContactForm(request.POST)
    # 中略
  else:
    form = ContactForm(request.GET or None)
    # 中略

request.GET request.POSTの内容

def index(request):
    print(request.GET)
    print(request.POST)
GET時
<QueryDict: {}>
<QueryDict: {}>

POST時
<QueryDict: {}>
<QueryDict: {'csrfmiddlewaretoken': ['<ハッシュ>'], 'subject': ['hello'], 'age': ['21']}>

request.GET 型でリクエストした場合は form.data がQueryDict型で初期化される。

request.POST は要するにリクエストペイロードが含まれる

QueryDict

dict型を継承した MultiValueDict をさらに継承したもの

immutableなので変更できない

views.py
def index(request):
    if request.method == 'POST':
        form = ContactForm(request.POST)
        form.data['age'] = 103 # こういうことをするとエラーになる

Boundfieldのデータを書き換えることもできない

form['age'].data = 103 # これもエラー

form.fields['age'].label = '年齢' # これはOK

form.fields['age'].initial = 100 # エラーにはならないが値は反映されない

ラベルのアップデート: バウンドされたfieldに定義してもfieldsオブジェクトに紐づいたものでもOK。両方書いた場合はBoundFieldsが優先される。

def index(request):
    ...
    form['age'].label = '年齢1'
    form.fields['age'].label = '年齢2'
    ...

# 年齢1が表示される

form.fieldsBoundField の関係

Boundfieldform.field への参照を持っているような形。

>>> form['age'].field
<django.forms.fields.IntegerField object at 0x10d906e10>
>>> form.fields['age']
<django.forms.fields.IntegerField object at 0x10d906e10>

同じオブジェクトを参照していることを確認。

まとめ的なもの

  • form: form周りのいろんな役割をするクラス
  • form.data: フォームのデータを格納。初期化時に定義し書き換え不可。
  • request.POST: リクエストデータが格納されて、これをformの引数として渡す。
  • form.fields: フィールドオブジェクトが格納された配列。
  • form.<キー名>: BoundField。formにバインドされたfield情報。加えてフィールドオブジェクトへの参照も持つ
8
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?