[Python]とにかくわかりやすく！Djangoでアプリ開発！ーその４ー


#前回の記事
前回の記事→[とにかくわかりやすく！Djangoでアプリ開発！ーその3ー](https://qiita.com/hiroyuki_mrp/items/a34a897be14d09082461)

#本記事の目的
python初心者の方が、本記事を見たあとに、一人でアプリ開発できることを目的にしております。
※インストールや開発環境については記載しません

#環境
macOSX　Sierra
python3.7
django 2.1.5

#前回まで
プロジェクトを立ち上げ(startproject)
→アプリの作成(startapp)
→view.pyを変更してレスポンスを書く
→urls.pyを修正する
→アプリの登録する
→index.html作る
→views.pyを直す
→htmlに変数入れる
→views.pyを直す
→複数ページ作るためにリンクつける
→views.pyを直す
→cssで装飾できるようにする
→htmlでフォームを作る
→views.pyを直す
→urls.pyを修正する
→やっぱりformクラスでフォームを作る
→views.pyを直す
→index.html直す
→urls.pyを元に戻す

とここまででした。

この続きとして[[Python]とにかくわかりやすく！Djangoでアプリ開発！ーその5ー](https://qiita.com/hiroyuki_mrp/items/344c3b3ce44564dad3a4)を書いたのですが、Viewのクラスについての描きたかったので、本記事を差し込みました。

#Viewのクラス

今の段階では

views.pyは以下のようになっているかと思います。

```python:views.py
from django.shortcuts import render
from django.http import HttpResponse
from .forms import AisatsuForm

def aisatsu(request):
    params = {
        'title':'Hello World',
        'msg':'ちゃんと挨拶したいので情報の登録をしてください',
        'form': AisatsuForm(),
    }
    if (request.method=='POST'):
        params['msg'] = 'こんにちは!'+request.POST['name']+'さん!<br>'+request.POST['area']+'にお住まいで<br>年齢は'+request.POST['age']+'歳なんですね!<br>よろしくお願いします。'
        params['form']= AisatsuForm(request.POST)
    return render(request,'app1/index.html', params)
```

これで悪くもないんですが、今はGETとPOSTだけですが、処理が増えた場合には、書くのが大変になりますね。
そこでDjangoで用意されているTemplateViewクラスを使ってみることにします。
TemplateviewクラスはViewを継承して用意されているものです。クラスとして使えるので、スッキリしたものになります。

```python:views.py
from django.shortcuts import render
from django.http import HttpResponse
from .forms import AisatsuForm
from django.views.generic import TemplateView

class AisatsuView(TemplateView):

    def __init__(self):
        self.params = {
            'title':'Hello World',
            'msg':'ちゃんと挨拶したいので情報の登録をしてください:',
            'form':AisatsuForm(),
        }

    def get(self,request):
        return render(request,'app1/index.html',self.params)

    def post(self,request):
        msg = 'こんにちは!'+request.POST['name']+'さん!<br>'+request.POST['area']+'にお住まいで<br>年齢は'+request.POST['age']+'歳なんですね!<br>よろしくお願いします。'
        self.params["msg"] = msg
        self.params['form'] = AisatsuForm(request.POST)
        return render(request,'app1/index.html',self.params)
```
`__init__`は初期化のメソッドです。paramsに初期設定を入れておきます。
getはそのままparamsをレンダリングし、POSTの場合は、POSTの内容を取り出してmsgを作成して、paramsの代入しています。

最後にurls.pyを修正します。viewsからAisatsuViewを呼び出せるようにし、urlpatternsに追加を行います。as_view()をくっ付けて置くことが重要です。

```python:app1/urls.py
from django.urls import path
from .views import AisatsuView #これ忘れずに

urlpatterns = [
    path("",AisatsuView.as_view(),name="aisatsu"),
]
```
ここまできたら、http://127.0.0.1:8000/app1/
にアクセスしてみます。

<img width="671" alt="スクリーンショット 2019-03-03 13.04.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/241505/29cfe308-33c2-7ddb-48e2-a8270dea9b4e.png">

見た目は一緒ですが、内側の処理自体はスッキリしましたね。
このように単純な処理であれば、関数で定義しても、クラスでやってもどちらでもいいかと思います。またGETメソッドしか使わないアプリであれば、関数だけでいいですね。


#フィールドを増やす

処理は色々できそうな感じになってきたので、フォームを改善します。
今はフォームの項目は以下のようになっていますね。charとintだけですね。

```python:forms.py
from django import forms

class Aisatsuform(forms.Form):
    name = forms.CharField(label="name")
    area = forms.CharField(label="area")
    age = forms.IntegerField(label="age")
```
正直これだけでもどうにかなりますが、便利なフォームが用意されているので、付け加えてみましょう。

##CharField/IntegerField/FloatField

入力必須や最少文字数、最多文字数を設定できます。
以下では最少文字数を設定してみました。
IntegerFieldをFloatFieldにすると小数点の数字も扱えます。

```python:forms.py
from django import forms

class AisatsuForm(forms.Form):
    name = forms.CharField(label="name",min_length = 3)
    area = forms.CharField(label="area")
    age = forms.IntegerField(label="age",min_length = 3)
```

##EmailField

Emailを入力するフィールドも簡単に作れます。@の入力がないとエラーが出ます。
※一応ちゃんと反映が確認できるように、views.pyのmsgは変更しています。

```python:forms.py
from django import forms

class AisatsuForm(forms.Form):
    name = forms.CharField(label="name",min_length = 3)
    area = forms.CharField(label="area")
    age = forms.IntegerField(label="age")
    mail = forms.EmailField(label="mail")
```

<img width="570" alt="スクリーンショット 2019-03-03 13.33.42.png" src="https://qiita-image-store.s3.amazonaws.com/0/241505/0fde6fdc-80ff-daff-2140-0b7509b056f0.png">

##URLField

urlに関するフィールドもつけられます。存在の有無ではなく、形式がURLかどうかをチェックしています。ここも当然入力必須有無と文字数制限ができます。

```python:forms.py
from django import forms

class AisatsuForm(forms.Form):
    name = forms.CharField(label="name",min_length = 3)
    area = forms.CharField(label="area")
    age = forms.IntegerField(label="age")
    mail = forms.EmailField(label="mail")
    url = forms.URLField(label="url")
```

<img width="644" alt="スクリーンショット 2019-03-03 13.47.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/241505/651bb3f9-a99d-0c53-80f8-9c76fbd567bf.png">

##DateField/TimeField/DateTimeField

せっかくなのでDateFieldを設定しています。

```python:forms.py
from django import forms

class AisatsuForm(forms.Form):
    
    date = forms.DateField(label="date")
    name = forms.CharField(label="name",min_length = 3)
    area = forms.CharField(label="area")
    age = forms.IntegerField(label="age")
    mail = forms.EmailField(label="mail")
    url = forms.URLField(label="url")
```

ちなみに入力形式は[yyyy-mm-dd]になります。スラッシュで入力する場合は[dd/mm/yyyy]になります。
<img width="652" alt="スクリーンショット 2019-03-03 13.57.29.png" src="https://qiita-image-store.s3.amazonaws.com/0/241505/ed438051-710b-bae3-8c57-a9251be87cc7.png">

##BooleanField

ブール値(True/False)ですが、いわゆるチェックボックスとして利用できます。
ここで大事なのはrequired=Falseとしているところです。入力必須としてしまうと、チェックが外して送信した時にエラーになってしまいます。
利用規約同意などであればいいですが、それ以外であれば、Falseにしないといけません。

```python:forms.py
from django import forms

class AisatsuForm(forms.Form):
    
    date = forms.DateField(label="date")
    name = forms.CharField(label="name",min_length = 3)
    area = forms.CharField(label="area")
    age = forms.IntegerField(label="age")
    mail = forms.EmailField(label="mail")
    url = forms.URLField(label="url")
    check = forms.BooleanField(label="check",required = False)
```

if文でhttpリクエストのcheckがTrueもしくはFalseであるときに、checkresultのパラメタに、それぞれ代入を行います。
checkresultのパラメタをさらにflagに代入します。それをmsgにくっつけました。

```python:views.py
from django.shortcuts import render
from django.http import HttpResponse
from .forms import AisatsuForm
from django.views.generic import TemplateView

class AisatsuView(TemplateView):

    def __init__(self):
        self.params = {
            'title':'Hello World',
            'msg':'ちゃんと挨拶したいので情報の登録をしてください:',
            'form':AisatsuForm(),
        }

    def get(self,request):
        return render(request,'app1/index.html',self.params)

    def post(self,request):
        if ('check' in request.POST):
            self.params['checkresult']='何かがOK'
        else:
            self.params['checkresult']='何かがNG'
        flag = self.params['checkresult']
        msg = 'こんにちは!'+request.POST['name']+'さん!<br>'\
            +request.POST['area']+'にお住まいで<br>年齢は'+request.POST['age']\
                +'歳なんですね!<br>今後の連絡先は'+request.POST['mail']\
                    +'ですね<br>よろしくお願いします。<br>URL:'+request.POST['url']\
                        +'<br>'+ request.POST['date'] + '<br>'+flag
        self.params["msg"] = msg
        self.params['form'] = AisatsuForm(request.POST)
        return render(request,'app1/index.html',self.params)
```

やらなくていいですが、
msgに付け加えなくても、チェックボックスの結果を表示することもできます。
表示なのでindex.htmlをいじればいいですね。

```html:index.html
{% load static %}
<!doctype html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <title>{{title}}</title>
        <link rel="stylesheet" type="text/css"
        href="{% static 'app1/css/style.css' %}"/>
    </head>
    <body>
        <h1>{{title}}</h1>　　　
        <p>{{msg|safe}}</p>
        <p>{{checkresult}}</p>  <!--これを増やしました-->
        <form action= "{% url 'aisatsu' %}" method = "post"> 
            {% csrf_token %} 
            {{form.as_ul}} 
            <p>   
            <input type="submit" value="入力"> 
        </form>
    </body>
</html>
```
チェック項目を表示できるようにできたので、あとはチェック項目を表示するための処理を記述します。views.pyを以下のように修正します。

```python:views.py
from django.shortcuts import render
from django.http import HttpResponse
from .forms import AisatsuForm
from django.views.generic import TemplateView

class AisatsuView(TemplateView):

    def __init__(self):
        self.params = {
            'title':'Hello World',
            'msg':'ちゃんと挨拶したいので情報の登録をしてください:',
            'form':AisatsuForm(),
        }

    def get(self,request):
        return render(request,'app1/index.html',self.params)

    def post(self,request):
        if ('check' in request.POST):
            self.params['checkresult']='何かがOK'
        else:
            self.params['checkresult']='何かがNG' #flag消しました

        msg = 'こんにちは!'+request.POST['name']+'さん!<br>'\
            +request.POST['area']+'にお住まいで<br>年齢は'+request.POST['age']\
                +'歳なんですね!<br>今後の連絡先は'+request.POST['mail']\
                    +'ですね<br>よろしくお願いします。<br>URL:'+request.POST['url']\
                        +'<br>'+ request.POST['date'] #変更しました

        self.params["msg"] = msg
        self.params['form'] = AisatsuForm(request.POST)
        return render(request,'app1/index.html',self.params)
```
処理の流れでいうと、if文でhttpリクエストのcheckがTrueもしくはFalseであるときに、checkresultのパラメタに、それぞれ代入を行います。それをhtmlに埋め込んだ流れです。

<img width="384" alt="スクリーンショット 2019-03-03 14.22.50.png" src="https://qiita-image-store.s3.amazonaws.com/0/241505/adf65c9f-36af-70b8-c802-c341efeee4f4.png">

#＃NullBooleanField

上記のBooleanではTrue/False以外の選択はなかったですが、「なんでもない」選択を作ることができます。
まずはforms.pyのFieldを修正します。

```python:forms.py
from django import forms

class AisatsuForm(forms.Form):
    
    date = forms.DateField(label="date")
    name = forms.CharField(label="name",min_length = 3)
    area = forms.CharField(label="area")
    age = forms.IntegerField(label="age")
    mail = forms.EmailField(label="mail")
    url = forms.URLField(label="url")
    check = forms.NullBooleanField(label="hoge")
```

NullBooleanでは選択が「はい」「いいえ」「不明」になっています。
「不明」:1
「はい」:2
「いいえ」:3
という番号が振られています。
2,3が選択された時の処理をviews.pyのpostに記述しておき、それ以外の時は(if文elseで書いてもいいですが)初期化`__init__`で記述しておきました。


```python:views.py
from django.shortcuts import render
from django.http import HttpResponse
from .forms import AisatsuForm
from django.views.generic import TemplateView

class AisatsuView(TemplateView):

    def __init__(self):
        self.params = {
            'title':'Hello World',
            'msg':'ちゃんと挨拶したいので情報の登録をしてください:',
            'checkresult':"未設定",
            'form':AisatsuForm(),
        }

    def get(self,request):
        return render(request,'app1/index.html',self.params)

    def post(self,request):
        if ("2" in request.POST["check"]):
            self.params['checkresult']='何かがOK'
        elif ("3" in request.POST["check"]):
            self.params['checkresult']='何かがNG'

        flag = self.params['checkresult']

        msg = 'こんにちは!'+request.POST['name']+'さん!<br>'\
            +request.POST['area']+'にお住まいで<br>年齢は'+request.POST['age']\
                +'歳なんですね!<br>今後の連絡先は'+request.POST['mail']\
                    +'ですね<br>よろしくお願いします。<br>URL:'+request.POST['url']\
                        +'<br>'+ request.POST['date'] + '<br>'+flag
        self.params["msg"] = msg
        self.params['form'] = AisatsuForm(request.POST)
        return render(request,'app1/index.html',self.params)
```

<img width="379" alt="スクリーンショット 2019-03-03 15.18.20.png" src="https://qiita-image-store.s3.amazonaws.com/0/241505/b5fd7eb5-fc2f-63c1-13a0-c73c49eb5a20.png">

##ChoiceField

いわゆる文字通り選択フィールドです。プルダウンになります。
ChoiceField()の引数で「choices=タプルのリスト」という引数を入れます。
直接見ていくほうがわかりやすいですね。
タプルの書き方は、(渡される値,選択する時のラベル)になります。

```python:forms.py
from django import forms

class AisatsuForm(forms.Form):

    list1 = [
        ('1','大変不満'),
        ('2','不満'),
        ('3','どちらでもない'),
        ('4','満足'),
        ('5','大変満足'),
    ]
    
    date = forms.DateField(label="date")
    name = forms.CharField(label="name",min_length = 3)
    area = forms.CharField(label="area")
    age = forms.IntegerField(label="age")
    mail = forms.EmailField(label="mail")
    url = forms.URLField(label="url")
    check = forms.NullBooleanField(label="hoge")
    choice = forms.ChoiceField(label="感想",choices = list1)
```

フォームが反映されるような処理に変更をするので、views.pyのpostの部分だけ書き直します。

```python:views.py
    def post(self,request):
        if ("2" in request.POST["check"]):
            self.params['checkresult']='何かがOK'
        elif ("3" in request.POST["check"]):
            self.params['checkresult']='何かがNG'

        flag = self.params['checkresult']

        msg = 'こんにちは!'+request.POST['name']+'さん!<br>'\
            +request.POST['area']+'にお住まいで<br>年齢は'+request.POST['age']\
            +'歳なんですね!<br>今後の連絡先は'+request.POST['mail']\
            +'ですね<br>よろしくお願いします。<br>URL:'+request.POST['url']\
            +'<br>'+ request.POST['date'] + '<br>'+flag\
            +'<br>満足度'+ request.POST['choice'] #追記
        self.params["msg"] = msg
        self.params['form'] = AisatsuForm(request.POST)
        return render(request,'app1/index.html',self.params)
```
感想を「どちらでもない」と設定したら、上部の所に満足度３というのがつきました。
<img width="381" alt="スクリーンショット 2019-03-03 16.27.22.png" src="https://qiita-image-store.s3.amazonaws.com/0/241505/40998416-0173-c3fd-e73f-f199e1dc6821.png">


さらに通常一行だけをを見ながら選択を行いますが、selectウィジェットを使うことで、プルダウンメニューの表示を変えることができます。

```python:forms.py
from django import forms

class AisatsuForm(forms.Form):

    list1 = [
        ('1','大変不満'),
        ('2','不満'),
        ('3','どちらでもない'),
        ('4','満足'),
        ('5','大変満足'),
    ]
    
    date = forms.DateField(label="date")
    name = forms.CharField(label="name",min_length = 3)
    area = forms.CharField(label="area")
    age = forms.IntegerField(label="age")
    mail = forms.EmailField(label="mail")
    url = forms.URLField(label="url")
    check = forms.NullBooleanField(label="hoge")
    choice = forms.ChoiceField(label="感想",choices = list1,widget=forms.Select(attrs={'size':５}))
```
こんな感じになりました。
↓↓↓
<img width="168" alt="スクリーンショット 2019-03-03 16.43.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/241505/a1262922-4424-e0fc-fb3e-1b63c6a68954.png">

複数項目を選択できるようにするためには、最後のウィジェットをMultipleにします。

```python:forms.py
    choice = forms.ChoiceField(label="感想",choices = list1,widget=forms.SelectMultiple(attrs={'size':5}))
``` 
複数選択しているので、当然表示への制御を変えなければいけません。views.pyを修正します。変更するのはPOSTの部分だけです。
やや難しいかもですが、request.POST.getlist("choice")はリストの形式を取っていますので、これを丸ごとitemsに代入します。
代入したものをインデックスで指定して、msgのなかで呼び出します。
※for文をコメントアウトしていますが、これを活用すれば、さらに処理を加えることも可能です。

```python:views.py
    def post(self,request):
        if ("2" in request.POST["check"]):
            self.params['checkresult']='何かがOK'
        elif ("3" in request.POST["check"]):
            self.params['checkresult']='何かがNG'

        flag = self.params['checkresult']

        items = request.POST.getlist('choice')
        #itemlist = []
        #for item in items:
        #    itemlist.append(item)

        msg = 'こんにちは!'+request.POST['name']+'さん!<br>'\
            +request.POST['area']+'にお住まいで<br>年齢は'+request.POST['age']\
            +'歳なんですね!<br>今後の連絡先は'+request.POST['mail']\
            +'ですね<br>よろしくお願いします。<br>URL:'+request.POST['url']\
            +'<br>'+ request.POST['date'] + '<br>'+flag\
            +'<br>満足度:'+ items[0]+','+items[1]+','+items[2]　#変更点

        self.params["msg"] = msg
        self.params['form'] = AisatsuForm(request.POST)
        return render(request,'app1/index.html',self.params)
```

また今回は詳細をかきませんが、forms.pyのChoiceFieldのウィジェットを以下を変更するだけで、ラジオボタンに変更することもできます。※その時は当然views.pyも変更する必要があります。

```python:forms.py
    choice = forms.ChoiceField(label="感想",choices = list1,widget=forms.RadioSelect())　#これ！！！
```
#この記事はここまで

続きはこちら→[[Python]とにかくわかりやすく！Djangoでアプリ開発！ーその5ー](https://qiita.com/hiroyuki_mrp/items/344c3b3ce44564dad3a4)
ここまでまとめはこちら→[[Python]とにかくわかりやすく！Djangoでアプリ開発！ー中間まとめ[1-4]ー](https://qiita.com/hiroyuki_mrp/items/e9ffd90cc19f097416b7)