DJango1.10 ログイン チュートリアル

###準備

pyenv等の隔離環境でdjangoのinstallを推奨します。
django1.10を対象に記載します。

```
$pip install django
```


####プロジェクトの作成
default_loginという名称でプロジェクトを作成します。

```
$mkdir django_login
$cd django_login
$django-admin.py startproject default_login
$cd default_login
$python manage.py migrate
$python manage.py createsuperuser
 （superuser作成）

$python manage.py runserver

```

http://127.0.0.1:8000/admin で管理画面にログインしてみます。
作成したユーザーとパスワードを利用します。


####アプリケーションの作成
accountsという名称でアプリケーションを作成します。

```

$python manage.py startapp accounts
$ls -a
(フォルダが作成されたか確認）

```

settings.pyに　accounts　を追加します。
ついでに日本語とタイムゾーンの設定も追加します。


```django_login/default_login/default_login/settings.py

INSTALLED_APPS = (
    :
    'accounts',
)

LANGUAGE_CODE = 'ja'
TIME_ZONE = 'Asia/Tokyo'

```

###URL
Djangoは標準でログインやログアウトのビューを用意してくれています。
url.pyでデフォルトのログインビューを利用してurlディスパッチを行います。

```django_login/default_login/default_login/urls.py
from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
    url('^', include('django.contrib.auth.urls')),

]

```
注意：Django1.10からログインビューにつきましてはurl(r'^accounts/login/$','django.contrib.auth.views.login')からurl('^',include('django.contrib.auth.urls'))へ変更されています。


urls.pyにinclude('django.contrib.auth.urls') を記述することで、認証関連のURLとそれに対応するビューが追加されます。
詳細は、django.contrib.auth.urlsの中に記載されています。
以下はdjango.contrib.auth.urlsの中身です。「password_change」等からわかるように、パスワード変更用のURLおよびビューが標準で提供されています。
（*今回は時間の制約上、ログインとログアウトのみの実装にとどめます。)

>
https://docs.djangoproject.com/en/1.10/topics/auth/default/#module-django.contrib.auth.views
^login/$ [name='login']
^logout/$ [name='logout']
^password_change/$ [name='password_change']
^password_change/done/$ [name='password_change_done']
^password_reset/$ [name='password_reset']
^password_reset/done/$ [name='password_reset_done']
^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$ [name='password_reset_confirm']
^reset/done/$ [name='password_reset_complete']

####Templateの作成
accountsの直下にtemplatesフォルダを生成します。


```
$cd accounts
$mkdir templates
```

templates直下にbase.htmlを作成します。
以下を記述します。

```django_login/default_login/accounts/templates/base.html
<!DOCTYPE html>
<html>
<head lang="ja">
  <meta charset="UTF-8">
  <title></title>
</head>
<body>
  Welcome Kobe Python MeetUp!
{% block content %}
{% endblock %}
</body>
</html>

```

defaultのログインビューを利用するためにtemplatesの下にregistrationフォルダを作成します。
registrationフォルダの下にlogin.htmlを作成します。
（注意：このディレクトリ構成と名前が必要です。）


```
mkdir registration
```

```django_login/default_login/accounts/templates/registration/login.html
{% extends "base.html" %}

{% block content %}
ai
{% if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}


<form method="post" action="{% url 'login' %}">
{% csrf_token %}
<table>
<tr>
    <td>{{ form.username.label_tag }}</td>
    <td>{{ form.username }}</td>
</tr>
<tr>
    <td>{{ form.password.label_tag }}</td>
    <td>{{ form.password }}</td>
</tr>
</table>

<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>

{# Assumes you setup the password_reset view in your URLconf #}
<p><a href="{% url 'password_reset' %}">Lost password?</a></p>

{% endblock %}
```

この時点でログインURLにアクセスしてログインしても何もおきません。(下記参照)
http://127.0.0.1:8000/login
※ログイン用のユーザIDとパスワードはsuperuser作成時の管理者アカウントを使います。
>
If called via POST with user submitted credentials, it tries to log the user in. If login is successful, the view redirects to the URL specified in next. If next isn’t provided, it redirects to settings.LOGIN_REDIRECT_URL (which defaults to /accounts/profile/). If login isn’t successful, it redisplays the login form.

ログインに成功するとhelloビューが呼ばれ、hello.htmlが表示されるようにします。
ログインした後のURLをsettings.pyに記載しビュー、テンプレートを次のように用意します。

```django_login/default_login/default_login/settings.py

# 追記
LOGIN_REDIRECT_URL = '/accounts/hello/'
LOGIN_URL = '/login'
```

accountsで始まるurlはaccouts/urlに飛ばすように下記追記します。

```django_login/default_login/default_login/urls.py
from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
    url('^', include('django.contrib.auth.urls')),
    url(r'^accounts/', include('accounts.urls',namespace='accounts')),


]

```

accouts以下でのurlの処理です。
accounts/helloとrequestがきた際にhelloビューが呼ばれるように設定します。
accountsフォルダの中にurls.pyを作成し以下を記載します。

```django_login/default_login/accounts/urls.py
from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'hello/$', views.hello, name='hello'),
]
```

hello.htmlを作成します。

```django_login/default_login/accounts/templates/hello.html
{% extends "base.html" %}

{% block content %}
<p><a href="{% url 'logout' %}?next={% url 'login' %}"> Log out</a></p>

<p>Hello! {{ name }}</p>
{% endblock %}
```

###ビューの作成
helloのビューを作成します。


```django_login/default_login/accounts/views.py

from django.shortcuts import render


# Create your views here.

def hello(request):
    name = request.user
    print(name)
    return render(request, 'hello.html', {'name': name})

```

これでログインに成功するとhello.htmlが表示され自分の名前が表示されるはずです。
python manage.py runserverを実行します。


###閲覧制限の追加
helloビューはログインしたユーザーにのみ閲覧可能にしていきます。
helloビューにlogin_requiredデコレーターを追加するだけでOKです。

```django_login/default_login/accounts/views.py

from django.shortcuts import render
from django.contrib.auth.decorators import login_required

# Create your views here.

@login_required(login_url='/login/')
def hello(request):
    name = request.user
    print(name)
    return render(request, 'hello.html', {'name': name})

```

ログインしていない状態でhttp://127.0.0.1:8000/accounts/hello
にアクセスするとloginページに遷移するようになります。

さらに認証を名前からEmailにかえてみます。

```django_login/default_login/default_login/backends.py　 

from django.contrib.auth.hashers import check_password
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend

class UserModelEmailBackend(ModelBackend):

    def authenticate(self, username="", password="", **kwargs):
        try:
            user = get_user_model().objects.get(email__iexact=username)
            if check_password(password, user.password):
                return user
            else:
                return None
        except get_user_model().DoesNotExist:
            # No user was found, return None - triggers default login failed
            return None
```

settings.pyに
AUTHENTICATION_BACKENDS = [
    'default_login.backends.UserModelEmailBackend',    # Login w/ email
]

を追記します。
これで、とりあえずEmailで認証することができるはずです。