Django forms.pyでフォームを使いこなす

この記事の内容
  • Djangoのフォームについて
  • forms.pyについて

この記事の対象者
  • Web開発に興味を持っている方
  • Djangoの学習者
  • Pythonエンジニア

この記事の構成
  1. forms.py
  2. widget-tweaks

この記事の前に読むとさらに良し!

Django: DBを扱うmodelsについて

Django: UpdateViewとCreateView

はじめに

前回の記事までは、DjangoのViewsのクラスビューについて触れてきました。
今回は、その発展形として、forms.pyについて触れていきたいと思います。
まずは、forms.pyに触れるためにDBの理解が必要になるので、以下に前回までの内容を踏まえて、models.pyを再度共有しておきます。

models.py

from django.db import models


class Category(models.Model):

    name = models.CharField(max_length=31, verbose_name="カテゴリ名")

    def __str__(self):
        return self.name

    class Meta:

        verbose_name_plural = "カテゴリ"


class Article(models.Model):

    title = models.CharField(max_length=62, verbose_name="タイトル")
    slug = models.SlugField(verbose_name="URLスラッグ(英語)")
    image = models.ImageField(upload_to="article_image", blank=True, verbose_name="記事のイメージ写真", help_text="登録しない場合は、デフォルトのイメージ写真を使用する")
    categories = models.ManyToManyField(Category, verbose_name="カテゴリ")
    body = models.TextField(verbose_name="本文")
    status = models.PositiveSmallIntegerField(default=1, verbose_name="公開ステータス", help_text="1:下書き, 2:公開")
    liked = models.IntegerField(default=0, verbose_name="いいね数")
    is_deleted = models.BooleanField(default=False, verbose_name="削除フラグ")
    creator = models.CharField(max_length=31, verbose_name="投稿者")
    created = models.DateTimeField(auto_now_add=True, verbose_name="投稿日時")
    modified = models.DateTimeField(auto_now=True, verbose_name="更新日")

    def __str__(self):
        return self.title

    class Meta:

        verbose_name_plural = "記事"



class Comment(models.Model):

    article = models.ForeignKey(Article, on_delete=models.CASCADE, verbose_name="記事", related_name="comments")
    commenter = models.CharField(max_length=31, verbose_name="コメント者名")
    body = models.TextField(verbose_name="コメント文")
    created = models.DateTimeField(auto_now_add=True, verbose_name="コメント投稿日時")

    def __str__(self):
        return self.article.title + ":{}のコメント".format(self.commenter)

    class Meta:

        verbose_name_plural = "コメント"

forms.py

Djangoでは、バックエンドでフォームを作成して、フロントに渡すことのできる機能が存在しています。

CreateViewやUpdateViewでは、fields = ()と言った形でmodels.pyに記載してあるフィールドのフォームを自動生成してくれますが、

このフォームの内容をカスタマイズしたり、models.pyに関係のないフォームをバックエンド側で作成することができます。

フローとしては以下になります。

フロー
1. forms.pyでフォームを作成
2. views.pyでforms.pyからフォームをimport
3. フロント側にフォームを投げる
4. form_valid でフォームリクエストを受け取る

と言った感じです。

2種類のフォームの例

models.pyをベースにフォームを作成

from django import forms
from .models import Article

class ArticleForm(forms.ModelForm):

    class Meta:
        model = Article
        fields = ('title', 'slug', 'body')

models.py関係なくフォームを作成

from django import forms

class ArticleForm(forms.ModelForm):

    title = forms.CharFIeld(label='タイトル', required=True)
    slug = forms.SlugField(label='URLスラッグ', required=True)
    body = forms.TextField(label='本文', required=True)

上記二つを組み合わせることも可能

from django import forms
from .models import Article

class ArticleForm(forms.ModelForm):

    class Meta:
        model = Article
        fields = ('slug', 'body')

    title = forms.CharField(label='タイトル', required=True)

views.pyでフォームを読み込み

from django.views.generic import CreateView
from django.urls import reverse_lazy
from .models import Article
from .forms import ArticleForm

class ArticleCreateView(CreateView):

    template_name = 'article/create.html'
    model = Article
    form_class = ArticleForm
    success_url = reverse_lazy('article:article_list')

article_create = ArticleCreateView.as_view()

注意点: models.pyを読み込まないformの場合は、def form_validで保存作業などを自分でする必要があります

バックエンド側でforms.pyでフォームを作成するメリットとデメリットは以下のような感じです。

バックエンドでフォームを作成するメリット

  • 細かなバリデーションをかけることができる
  • views.pyとバリデーションを切り離すことでコードが綺麗になる

バックエンドでフォームを作成するデメリット

  • フォームのデザインを弄りにくい
  • 自由度が低い

上記を踏まえて、forms.pyを活用するケースは個人的にはただ一つです。

細かなバリデーションをかける必要があるか

inputの値に制約をかけたい場合、またフロント側のJSでなく、PythonでDBの値に応じて制約をかけたい場合、forms.pyを使うと良いと言えます。

そして、上述したデメリットは、次に述べるwidget-tweaksという外部ライブラリを使うことでほぼほぼ回避できます。

widget-tweaks

widget-tweaksは、Djangoで生成するformに対して、HTML内でIDを加えたり、styleを足したり、classを足したり、placeholderやその他様々な値を付け足すことができます。

widget-tweaksをinstall

pip install django-widget-tweaks

settings.py

INSTALLED_APPS = [
    ...
    ...
    'widget_tweaks',
]

form.html

{% load static widget_tweaks %}

{{ form.title|add_class:'p-1 rounded border'|attr:'placeholder:記事のタイトル' }}

{{ form.slug|attr:'style:padding:4px;'|attr:'required' }}

{{ form.body|attr:'id:article-body' }}

上記のHTML内では、idを付与したり、classをつけたり、styleをつけたり、様々なことをしています。
使い方さえわかれば、widget-tweaksは、Django固有のフォームを簡単にカスタマイズできるようにしてくれます。

widget-tweaksのよく使う機能

  • |add_class:''
    ''の中にclass名をつけることができる。主にUIを変更するときにclass経由で行うのがbest
  • |attr:''
    inputに様々な要素(attribute)を設定することができる。
    例: placeholder, required, selected, checked, id, style

attrの場合、' 'の中で再度' 'を使うとエラーになるので注意

終わりに

今回は、Djangoのforms.pyについてと便利なライブラリのwidget-tweaksについて紹介しました。

forms.pyの最も大きなメリットはバリデーションをバックエンド側でかけることができることです。

次の記事で、forms.pyのバリデーションについて解説していきたいと思います。

お疲れ様でした!