見出し画像

【82日目】Django_formsを使いこなす_ラジオボタン_プログラミング学び日記

 このnoteは、31歳未経験からエンジニアを目指して勉強していく記録を綴っているものです。現在はAdTechでカスタマーサクセスを担当しつつ、色んなチャンスに恵まれ、CS業務や子育てと並行しながらチャレンジしています。

 これからプログラミングを始める方にとってのTipsやモチベアップに繋げられるように頑張りたいと思っています。
--------------------------------------------


今日からこちらのTodoアプリをブラッシュアップしていく取組です。元のソースコードはこちらです。


最初の取り組みとして、業務種別や処理状況の項目をラジオボタンで表示するように改造してみたいと思います。


一見簡単そうですが、これだけでも意外と大変でした。なぜなら以下のような状況でした。

①入力方法の見た目を変えるにはフォームを使わないとダメそう
②しかしTodoアプリは元々フォームを使っていなかった(モデルのフィールドを直接表示しているだけ)
③この場合に使う「ModelForm」はまだ使ったことがなかった


ModelFormの使い方を調べながら、一つずつ解決していきました。


forms.pyでラジオボタンを定義(ModelFormクラス)

早速ModelFormを使います。Todoモデルのうち「category」と「status」の2つをラジオボタンで表示させるため、フォームの中で設定します。「モデルと同じフィールド名 = ModelChoiceField」で設定できます。当然ですが、モデルと同じフィールド名で設定しなければ設定内容は反映されません。

また、クラス内にMetaクラスを作ることで、フォームとモデルを紐づけることができます。フォーム化するフィールドも指定可能です(今回は全てなので「__all__」にしています)。

更にここでは初期化(init)も設定しました。フォームの大きさを揃えるform-controlを渡しています。

[forms.py] ←今回の主役
 
from django import forms
from .models import Todo, TodoCategory, TodoStatus

class TodoForm(forms.ModelForm):

    category = forms.ModelChoiceField(
        label="業務種別",
        queryset=TodoCategory.objects.all(),  # 元となるモデルを指定
        widget=forms.RadioSelect,             # ここでラジオボタンを設定!
        empty_label=None,)
    
    status = forms.ModelChoiceField(
        label="処理状況",
        queryset=TodoStatus.objects.all(),   # 元となるモデルを指定
        widget=forms.RadioSelect,            # ここでラジオボタンを設定!
        empty_label=None,)
    
    class Meta:
        model = Todo
        fields = '__all__'
    
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['title'].widget.attrs['class'] = 'form-control'
        self.fields['description'].widget.attrs['class'] = 'form-control'
        self.fields['deadline'].widget.attrs['class'] = 'form-control'
[models.py] ←大前提となるモデル(DBのテーブル)はこちら
 
from django.db import models
 
~中略~
 
class Todo(models.Model):
    title = models.CharField(
        "タスク名", 
        max_length=30,)
    description = models.TextField(
        "詳細",
        blank=True,)
    deadline = models.DateField(
        "締切")
    category = models.ForeignKey(
        TodoCategory,
        on_delete=models.CASCADE,
        verbose_name="業務種別")
    status = models.ForeignKey(
        TodoStatus,
        on_delete=models.CASCADE,
        verbose_name="処理状況")

    def __str__(self):
        return self.title



views.pyにformを紐づける

以前までのビューにformを紐づけるためのコードを加えました。ポイントは、モデルもフォームもどちらも紐づける点と、CreateViewなどでフィールドを指定しない点でした。後者はフォームで指定されているためだと思われます。

[views.py]
 
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView

from .models import Todo
from .forms import TodoForm


~中略~


class TodoCreate(CreateView):
    model = Todo
    form_class = TodoForm  
    # fields = "__all__"  ※fieldは指定しないためコメントアウトした
    success_url = reverse_lazy("list")


class TodoUpdate(UpdateView):
    model = Todo
    form_class = TodoForm
    # fields = "__all__"  ※fieldは指定しないためコメントアウトした
    success_url = reverse_lazy("list")

普段はviews.pyを設定後にurls.pyでパスを設定しますが、今回はパスに変更は無いため省略します。


テンプレートを編集してラジオボタンを横に表示

本日一番時間がかかったのがここでした…!何故なら上記のように進めてくると、ラジオボタン部分が以下になるためです。

縦に並んで長くなりますし、横の中点が邪魔で仕方ない。。これは元々テンプレート側で「form.as_table」で表示しており、その場合デフォルトではhtmlの<ul>/<li>タグになってしまうからです。

色々調べると、Djangoのas_tableやas_pは簡単に使えますが、その分柔軟性に欠けるらしく、細かく変更を加えたい場合には適していないみたいです。

そこで、今回は初めて&フィールド数も少ないので、、力業で全て個別に記載してしまうことにしました。工夫の余地はありそうなので、良い方法が見つかり次第試していこうと思います。

[todo_form.html]
 
{% extends 'todo/base.html' %}

{% block header %}
<h1>タスクの作成/編集</h1>
{% endblock header %}

{% block content %}
<form action="" method="POST">{% csrf_token %}
    {{ form.non_field_errors }}
        <table>
            <tbody>
                <tr>
                    <th width="150">{{ form.title.label_tag }}</th>
                    <td>{{ form.title }}</td>
                </tr>
                <tr>
                    <th width="150">{{ form.description.label_tag }}</th>
                    <td>{{ form.description }}</td>
                </tr>
                <tr>
                    <th width="150" height="50">{{ form.deadline.label_tag }}</th>
                    <td>{{ form.deadline }}</td>
                </tr>
                <tr>
                    <th width="150" height="50">{{ form.category.label_tag }}</th>
                    <td>
                        {% for radiobutton in form.category %}
                            {{ radiobutton }} 
                        {% endfor %}
                    </td>
                </tr>
                <tr>
                    <th width="150">{{ form.status.label_tag }}</th>
                    <td>
                        {% for radiobutton in form.status %}
                            {{ radiobutton }} 
                        {% endfor %}
                    </td>
                </tr>
            </tbody>
        </table>
<br>
   <a class="btn btn-outline-secondary" href="{% url 'list' %}" role="button">戻る</a>
   <input class="btn btn-success" type="submit" Value="送信">
</form>
{% endblock content %}

上記で特に学びになったのが、「form」という変数の中のデータをどうやったら個別に取り出せるのか分かったことです。「form.フィールド名」で各データにアクセスできます。また、ラベルを定義していた場合は「form.フィールド名.label_tag」でOKです。

また、<tr> / <th> / <td>などのタグの使い方を確認したり、微妙な大きさや幅の調整で「ディベロッパーツール」を使ってみました。これは想像以上に便利でした…!
もっと使いこなしていきたい。



参考


これまで修了したコース等

【YouTube_Django関係】
Pythonでウェブサービスを作ろう! #1
テンプレートをマスターしよう! #2
静的ファイルを配信しよう !#3
本番公開しよう! #4
データベースと接続しよう! #5
ブログを作って学ぶモデル入門! #6
これが汎用ビューの力! #7
Djangoフォームを自由自在に操ろう! #8
djagoを最大限使って効率よくログインを作ろう! #9
ログイン完成!サインアップ & メール認証 #10
データベースマイグレーション前編 #15
データベースマイグレーション後編 #16

【Paiza】
Aランクレベルアップメニュー 24/49問
データセット選択メニュー 4/17問
配列メニュー      59/64問
ループメニュー1 20/20問
ループメニュー2 12/20問
条件分岐メニュー    25/25問
二重ループメニュー   19/19問
配列活用メニュー    26/26問
文字列処理メニュー   30/30問
Bランクレベルアップメニュー 62/62問
Cランクレベルアップメニュー 30/30問
ランクB合格
ランクC合格

【書籍/ブログ】
Django入門 | 初心者でも1時間でWebアプリ(Todoアプリ)を作成するコース
基礎からのMySQL     514/514頁
Web技術の基本      189/189頁 ※2周目中
京大のPython教科書    116/201頁
Pythonデータベースプログラミング 194/194頁
Pythonエンジニアファーストブック読了

【Progate】
Python Ⅰ~Ⅴ
Python アプリ版 コースⅠ~Ⅴ
SQL Ⅰ~ Ⅳ
SQL アプリ版 コースⅢ
HTML&CSS 初級編

【その他】
Pythonの環境構築
VSCodeの環境構築
MySQLの環境構築(MAMP)
Git / GitHubの環境構築
HEROKUの環境構築

この記事が気に入ったらサポートをしてみませんか?