django#007_MessagesとTemplateグループ化

#前回まで
前回はUser Objectについて話しましたので、今回はメッセージの表示とか、あとtemplatesの中にあるHTMLページをまとめる方法を書きたいと思います。Registerシステムがあれば、Login成功とか、Login失敗するときは色んな表示メッセージがくるじゃないですか？
# 流れ
* view.pyでLoginの動作などをしたのあとのメッセージ追加
* templates  の中でメッセージ追加
* templatesのHTMLをグループ化する
 

## view.pyでLoginの動作などをしたのあとのメッセージ追加
まず messages frameworkを見てきましょう。
WebをみるときたまにPOP-UPをみたことがありますか？一回だけでClickすればなくなる。それはこのmessages frameworkの役目ですね。中にはレベルに分けておいて（info,warning,errorなど）
メッセージのレベルは以下のようになります：

DEBUG：いわゆる開発中のとき使われてるのMessageですね。
INFO:Userにお知らせする。
SUCCESS：Userがやってたアクション成功した（Loginしましたとか）
WARNING:エラーにはならなく実行しましたが、まぁ、英語通り、警告って感じですね。
ERROR：アクション実行できませんでした。なにかエラーが起こってる。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/154661/f90dc044-a0de-855a-6c61-925d278318b5.png)
以下の例は、
User登録成功：info,success
User 登録失敗：error
User　Login in成功：info

あと前回説明抜けところがありますね。

```
Form.is_valid()
```

これを使うことによってFormのデータが有効かどうかをCheckしてくれますね～もし無効なデータがあるならFalseに戻し、OKなデータならTrueが戻してくれます。

```
Form.cleaned_data
```

DjangoのForm 　FrameworkはデータがOKかどうかを判断するだけではなく、データ自体も“綺麗”にしてくれます、データを一致のFormatに変更しれくれます。例えば、DateFieldをPythonのdatetime.date　Objectに変更し、“'1994-07-15'”がもらってでもちゃんとPythonのObjectに変更しれくれます。多分言葉だけ説明してもちょっとわかりつらいから、Shellから説明します。

```
python manage.py shell
```

djangoからforms をImportし、新しいforms を１個作ります。
fieldsは文字200までのsubjとメールアドレスですね。

```
In [6]: from django import forms                                                
In [7]: class form1(forms.Form): 
   ...:     subj=forms.CharField(max_length=100) 
   ...:     email=forms.EmailField() 
```

データ新しく作ります。そして新しいform1 “f”が作りました。

```
In [8]: data1={'subj':'test1', 
   ...:        'email':'abc@example.com'}  
In [9]: f=form1(data1) 

```
ではまず先のis_valid()を試してみよう。
うんうん、OKですね。無効な入力が入ってません。

```
In [13]: f.is_valid()                                                           
Out[13]: True
```

次はclean()を使ってみます。
うん…あまり変わらないですね。

```
In [14]: f.clean()                                                              
Out[14]: {'subj': 'test1', 'email': 'abc@example.com'}
```
じゃ今度はもう一つのform “form2”を作ってみますね。
文字200まで、メールアドレス、更に時間の入力も入ってます。

```
In [27]: class form2(forms.Form): 
    ...:     subj=forms.CharField(max_length=100) 
    ...:     email=forms.EmailField() 
    ...:     date=forms.DateField() 
```
ではでは、また新しいデータを作り、f2が定義されました。

```
In [28]: data2={'subj':'subj2', 
    ...:        'email':'example@example.com', 
    ...:        'date':'1998-02-11' 
    ...:      }  
```

In [29]: f2=form2(data2)    

まずデータが有効かどうかをCheckします。

```                                                    
In [30]: f2.is_valid()                                                          
Out[30]: True
```

clean()かけるまえのデータは、辞書ですね。

````
In [42]: f2.data                                                                
Out[42]: {'subj': 'subj2', 'email': 'example@example.com', 'date': '1998-02-11'}
```

次はclean()をかけてみます。
出力の変化見えましたか？dateの項目がdatetime.dateになりました！

```
In [31]: f2.clean()                                                             
Out[31]: 
{'subj': 'subj2',
 'email': 'example@example.com',
 'date': datetime.date(1998, 2, 11)}
In [33]: f2.cleaned_data.get('date')                                            
Out[33]: datetime.date(1998, 2, 11)
```

念のため、Typeを確認しよう。OKですね！

```
In [43]: d=f2.cleaned_data.get('date')                                          
In [44]: d                                                                      
Out[44]: datetime.date(1998, 2, 11)
In [45]: type(d)                                                                
Out[45]: datetime.date
```

最後はメールアドレスのところに変なもの入れてみよう。

```
In [36]: data3={'subj':'subj2',  
    ...:     ...:        'email':'examplexample.com',  
    ...:     ...:        'date':'1998-02-11' 
    ...:     ...:      }  
```
is_valid()  はちゃんとFalse返してくれたね～

```
In [37]: f3=form2(data3)   
In [38]: f3.is_valid()                                                          
Out[38]: False
```

話が長くなりましたが、コードは以下になります：

```

from django.shortcuts import render,redirect
from django.http import HttpResponse
from .models import mydb
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth import login,logout,authenticate
from django.contrib import messages

def homepage(request):
	return render(
		request=request
		,template_name="helloworld/home.html"
		,context={"mydb":mydb.objects.all}
		)

def register(request):

	if request.method == "POST":
		form=UserCreationForm(request.POST)
		if form.is_valid():
			user=form.save()
			username=form.cleaned_data.get('username') #
			messages.success(request,'Your account is Created,{}.'.format(username))#
			login(request,user)
			messages.info(request,'Logged In,{}.'.format(username))#
			return redirect("helloworld:homepage")
		else:
			for msg in form.error_messages:
				print(form.error_messages[msg])
				messages.error(request,'some error,{}.',format(form.error_messages[msg]))#



	form =UserCreationForm

	return render(
		request=request
		,template_name="helloworld/register.html"
		,context={"form":form}

		)
```
## templates  の中でメッセージ追加
次はheader.htmlの中にmessageを入れます。
POP UP　Messageの出し方はFrameworkによって違いますので、ここはw3.cssの参考例になります。views.pyからmessage をもらって、このmessageの属性によって出てくるのメッセージがわかります。

```
<head>
	{% load static %}
	<link rel='stylesheet' href="{% static "helloworld/w3c.css"%}">
</head>

<body>

    <div class="w3-bar w3-border w3-light-grey">
        <a href="/" class="w3-bar-item w3-button w3-mobile">Home</a>
        <a href="/register" class="w3-bar-item w3-button w3-mobile">Register</a>
        <a href="/login" class="w3-bar-item w3-button w3-mobile">login</a>
    </div>

          {% if messages %}
        {% for message in messages %}
            {% if message.tags == 'success'%}
                <div class="w3-panel w3-green">

                    <h3>success</h3>
                    <p>{{message}}</p>
                          <span onclick="this.parentElement.style.display='none'"
                    class="w3-button w3-display-topright"
                    >&times;</span>
                </div>
            {% elif message.tags == 'info'%}
                <div class="w3-panel w3-blue">

                    <h3>Info</h3>
                    <p>{{message}}}</p>
                          <span onclick="this.parentElement.style.display='none'"
                    class="w3-button w3-display-topright"
                    >&times;</span>
                </div>
            {% elif message.tags == 'warning'%}
                <div class="w3-panel w3-red">

                    <h3>Warning</h3>
                    <p>{{message}}</p>
                          <span onclick="this.parentElement.style.display='none'"
                    class="w3-button w3-display-topright"
                    >&times;</span>
                </div>
            {% elif message.tags == 'error'%}
                <div class="w3-panel w3-yellow">

                    <h3>Error</h3>
                    <p>{{message}}</p>
                          <span onclick="this.parentElement.style.display='none'"
                    class="w3-button w3-display-topright"
                    >&times;</span>
                </div>
            {% endif %}
        {% endfor %}
    {% endif %}

  {% block content%}}
  {% endblock%}

</body>
```
Userを登録成功したら、SuccessとInfoのメッセージがちゃんと出ましたね！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/154661/e6f7c1bd-77bd-854a-a588-76e4f9708d4f.png)
もしなにかの入力間違いだったら…
![image.png](https://qiita-image-store.s3.amazonaws.com/0/154661/3f442e6f-a74d-760f-a5e1-425246d5a053.png)


## templatesのHTMLをグループ化する
こっちらは先までのheader.htmlですが、めっちゃ長いと思いませんか？
ここで”include”というものを紹介します。
ここからみると、Navバーがあって、Messageのエリアもあっての感じですね。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/154661/6f73a182-c449-094b-53ba-21ffa7e91984.png)
じゃこんな風に分けたらどうです？Layoutも綺麗になりますね。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/154661/8790e23a-ad7b-a553-7ddd-e6f8029e84a4.png)

nav.htmlを開いて、header.htmlの中にあるNavの部分をここで貼り付けます。

```
  <nav>
    <div class="w3-bar w3-border w3-light-grey">
        <a href="/" class="w3-bar-item w3-button w3-mobile">Home</a>
        <a href="/register" class="w3-bar-item w3-button w3-mobile">Register</a>
        <a href="/login" class="w3-bar-item w3-button w3-mobile">login</a>
    </div>
  </nav>
```

message.htmlを開いて、先POPUPの部分だけここに貼り付けます。

```
  {% if messages %}
        {% for message in messages %}
            {% if message.tags == 'success'%}
                <div class="w3-panel w3-green">

                    <h3>success</h3>
                    <p>{{message}}</p>
                          <span onclick="this.parentElement.style.display='none'"
                    class="w3-button w3-display-topright"
                    >&times;</span>
                </div>
            {% elif message.tags == 'info'%}
                <div class="w3-panel w3-blue">

                    <h3>Info</h3>
                    <p>{{message}}}</p>
                          <span onclick="this.parentElement.style.display='none'"
                    class="w3-button w3-display-topright"
                    >&times;</span>
                </div>
            {% elif message.tags == 'warning'%}
                <div class="w3-panel w3-red">

                    <h3>Warning</h3>
                    <p>{{message}}</p>
                          <span onclick="this.parentElement.style.display='none'"
                    class="w3-button w3-display-topright"
                    >&times;</span>
                </div>
            {% elif message.tags == 'error'%}
                <div class="w3-panel w3-yellow">

                    <h3>Error</h3>
                    <p>{{message}}</p>
                          <span onclick="this.parentElement.style.display='none'"
                    class="w3-button w3-display-topright"
                    >&times;</span>
                </div>
            {% endif %}
        {% endfor %}
    {% endif %}
```
最後に、header.htmlは先作ったのものを“include”すれば、完成です。

```
<head>
	{% load static %}
	<link rel='stylesheet' href="{% static "helloworld/w3c.css"%}">
</head>

  {% include "helloworld/includes/nav.html" %}
  {% include "helloworld/includes/message.html" %}

  {% block content%}}
  {% endblock%}

</body>

```

以上、お疲れ様です！