Help us understand the problem. What is going on with this article?

Django forms.Formを用いたフォームの作成

More than 1 year has passed since last update.

はじめに

 Djangoの初学者はまずDjango公式サイトやDjangogirlsなどのチュートリアルを取り組むのではないか。
 そこで共通するのは、フォームの事例が、forms.ModelFormを利用したものに偏っており、当たり前のようにforms.ModelFormを選択していた。しかし、フォームをオリジナルあるものにするには、forms.Formを使う手があるが、解説した記事が少ないと感じていた。事例を交えて記事に残そうと思う。

環境

Windows10
Python 3.6.0
Django 2.1.1

models.pyの編集

ModelにBookモデルを作成する。タイトル、著者、所有者、貸出先、発刊日、購入日、ISBN、備考を属性として追記する。

models.py
from django.db import models

class Book(models.Model):
    LIBRARY_CHOICES = (
        ('国立図書館', '国立図書館'),
        ('県立図書館', '県立図書館'),
        ('市立図書館', '市立図書館'),
    )

    title = models.CharField(max_length=200)
    author = models.CharField(max_length=50)
    holder = models.CharField(max_length=10, choices=LIBRARY_CHOICES)
    user = models.CharField(max_length=50, default='admin')
    published_date = models.DateField(blank=True, null=True)
    purchased_date = models.DateField(blank=True, null=True)
    isbn = models.BigIntegerField()
    comment = models.TextField(blank=True, null=True)

    def __str__(self):
        return self.title

フォームの編集

ここでは、本の登録フォームを例とした。

forms.py
from django import forms

LIBRARIES_CHOICES = (
    ('国立図書館', '国立図書館'),
    ('県立図書館', '県立図書館'),
    ('市立図書館', '市立図書館'),
)

class BooksForm(forms.Form):
    title = forms.CharField(
        label='タイトル',
        max_length=200,
        required=True,
    )

    author = forms.CharField(
        label='著者',
        max_length=50,
        required=True,
    )

    holder = forms.ChoiceField(
        label='所有者',
        widget=forms.Select,
        choices=LIBRARIES_CHOICES,
        required=True,
    )

    user = forms.CharField(
        label='貸出先',
        max_length=50,
        required=True,
    )

    published_date = forms.DateTimeField(
        label='発刊日',
        required=True,
        widget=forms.DateInput(attrs={"type": "date"}),
        input_formats=['%Y-%m-%d']
    )

    purchased_date = forms.DateTimeField(
        label='購入日',
        required=True,
        widget=forms.DateInput(attrs={"type": "date"}),
        input_formats=['%Y-%m-%d']
    )

    # 日付だけでなく、日時にする場合は以下
    # datetime = forms.DateTimeField(
    #    label='日時',
    #    required=True,
    #    widget=forms.DateTimeInput(attrs={"type": "datetime-local"}),
    #    input_formats=['%Y-%m-%dT%H:%M']
    #)

    isbn = forms.IntegerField(
        label='isbn',
        required=False,
    )

    comment = forms.CharField(
        label='備考',
        max_length=1000,
        required=False,
        widget=forms.TextInput()
    )

django.forms.ModelFormでforms.pyを作成すると、forms.ModelFormを利用すると以下4行程度だが、モデルに紐づいているため応用が効きにくい

forms.py
from django import forms
 from .models import Book

 class BookForm(forms.ModelForm):
     class Meta:
         model = Book
         fields = ('title', 'author')

コントローラ(Views)の編集

本の登録を行うコントローラを関数ベースで定義する。
フォームからPOSTがなければ、formをそのままbook_new.htmlに渡す。
フォームに入力があり、POSTを送信した場合、入力内容をバリデートする。バリデートが通った場合、bookオブジェクトをcreateする。
その後、本一覧ページにredirectしている。(redirect先である、book_list.htmlは省略)

views.py
from django.shortcuts import render, get_object_or_404, redirect
from .models import Book
from .forms import BooksForm

def book_new(request):
    form = BooksForm(request.POST or None)
    if form.is_valid():
        book = Book()
        book.title = form.cleaned_data['title']
        book.author = form.cleaned_data['author']
        book.holder = form.cleaned_data['holder']
        book.user = form.cleaned_data['user']
        book.published_date = form.cleaned_data['published_date']
        book.purchased_date = form.cleaned_data['purchased_date']
        book.isbn = form.cleaned_data['isbn']
        book.comment = form.cleaned_data['comment']

        Book.objects.create(
            title=book.title,
            author=book.author,
            holder=book.holder,
            user=book.user,
            published_date=book.published_date,
            purchased_date=book.purchased_date,
            isbn=book.isbn,
            comment=book.comment,
        )
        return redirect('book:book_list')
    return render(request, 'book/book_new.html', {'form': form})

テンプレートの編集

本の登録を行うテンプレートを準備
※継承元のテンプレートbook/base.htmlは省略

book_new.html
{% extends 'book/base.html' %}

{% block content %}
    <h1>New book</h1>
  <table class="table">
    <form method="post" action="">
    {% csrf_token %}
    <tbody>
      <tr>
        <th>タイトル</th>
        <td>{{ form.title }}</td>
      </tr>
      <tr>
        <th>著者</th>
        <td>{{ form.author }}</td>
      </tr>
      <tr>
        <th>所有者</th>
        <td>{{ form.holder }}</td>
      </tr>
      <tr>
        <th>貸出先</th>
        <td>{{ form.user }}</td>
      </tr>
      <tr>
        <th>発刊日</th>
        <td>{{ form.published_date }}</td>
      </tr>
      <tr>
        <th>購入日</th>
        <td>{{ form.purchased_date }}</td>
      </tr>
      <tr>
        <th>ISBN</th>
        <td>{{ form.isbn }}</td>
      </tr>
      <tr>
        <th>備考</th>
        <td>{{ form.comment }}</td>
      </tr>
    </tbody>
      <tr>
      <td><input class="btn btn-primary" type="submit" value="Submit">
        <input class="btn btn-secondary" type="reset" value="Reset">
      </td>
      <td>
      </td>
      </tr>
    </form>
  </table>

{% endblock %}

画面はこんなイメージ
new_book.PNG

おわりに

django.forms.ModelFormでforms.pyを作成すると簡単に書くことができるが、モデルと紐づけしないフォームを作成する上で、django.forms.Formでのコーディングは必要となる。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away