Django - User

Lecturer:Yan

2020 / 5 / 31

  • Login

  • Logout

  • Registration

  • Social Login

Outline

Before We Start

進到虛擬環境中

$ cd 到你的資料夾
$ venv\Scripts\activate
$ cd 到上次你的資料夾
$ source venv/bin/activate

for Windows

for Mac

使用 django.contrib 套件中的 auth app

確認裡面有 'django.contrib.auth'

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth', # 確認有這句
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'restaurants',
]

打開 mysite/settings.py

一樣在 mysite/settings.py

MIDDLEWARE = [
  'django.middleware.security.SecurityMiddleware',
  'django.contrib.sessions.middleware.SessionMiddleware',
  'django.middleware.locale.LocaleMiddleware',
  'django.middleware.common.CommonMiddleware',
  'django.middleware.csrf.CsrfViewMiddleware',
  'django.contrib.auth.middleware.AuthenticationMiddleware', # 確認有這句  
  'django.contrib.messages.middleware.MessageMiddleware',
  'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

確認裡面有

'django.contrib.auth.middleware.AuthenticationMiddleware'

修改 urls.py

# 確認這些都有引入
from django.contrib import admin
from django.urls import path, include
...

urlpatterns = [
	...
    path('accounts/', include('django.contrib.auth.urls')), # 新增
]

路徑:mysite / urls.py

django.contrib.auth.urls

from django.contrib.auth import views
from django.urls import path

urlpatterns = [
    path('login/', views.LoginView.as_view(), name='login'),
    path('logout/', views.LogoutView.as_view(), name='logout'),

    path('password_change/', views.PasswordChangeView.as_view(), name='password_change'),
    path('password_change/done/', views.PasswordChangeDoneView.as_view(), name='password_change_done'),

    path('password_reset/', views.PasswordResetView.as_view(), name='password_reset'),
    path('password_reset/done/', views.PasswordResetDoneView.as_view(), name='password_reset_done'),
    path('reset/<uidb64>/<token>/', views.PasswordResetConfirmView.as_view(), name='password_reset_confirm'),
    path('reset/done/', views.PasswordResetCompleteView.as_view(), name='password_reset_complete'),
]

這是它的原始碼

accounts/login/ [name='login']
accounts/logout/ [name='logout']
accounts/password_change/ [name='password_change']
accounts/password_change/done/ [name='password_change_done']
accounts/password_reset/ [name='password_reset']
accounts/password_reset/done/ [name='password_reset_done']
accounts/reset/<uidb64>/<token>/ [name='password_reset_confirm']
accounts/reset/done/ [name='password_reset_complete']

因此當我們定義好了

path('logout/', views.LogoutView.as_view(), name='logout')

就是額外新增了以下這些路徑可以使用

Create Login page

先在 mysite / templates 中新建資料夾 registration

目前在 template 資料夾中的結構
├── base.html
├── home.html
├── post.html
├── post_delete.html
├── post_edit.html
├── showImg.html
└── registration (新增資料夾)
   └── login.html (新增檔案)

新建 login.html

{% extends "base.html" %}
{% block content %}
    <h1>Login</h1>
    <form action="{% url 'login' %}" method="post" class="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">
        <a href="{% url 'password_reset' %}">忘記密碼?</a>
    </form>
{% endblock %}

路徑:mysite / templates / registration / login.html

login.html 的另一種寫法

{% extends "base.html" %}
{% block content %}
    <h1>Login</h1>
    <form action="{% url 'login' %}" method="post" class="login">
        {% csrf_token %}
        <table>
            {{ form.as_table }}
        </table>
        <input type="submit" value="login">
        <a href="{% url 'password_reset' %}">忘記密碼?</a>
    </form>
{% endblock %}

檔案路徑:mysite / templates / registration / login.html

這邊用了 {{ form.as_table }} 來代替了前面一串

<table>
	{{ form.as_table }}
</table>
<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>

要來              解釋程式碼啦

<form action="{% url 'login' %}" method="post" class="login">
{% url '字串' %}

Django 會將這裡的字串對應到符合 name 參數的 url pattern

例如  {% url 'login' %}  即為對應到

path('accounts/login/', views.LoginView.as_view())

<a href="{% url 'password_reset' %}">忘記密碼?</a>

所以這裡的 {% url 'password_reset' %} 就會對應到

重設密碼的 url pattern

{% csrf_token %}

為了防範 CSRF 攻擊因此加上這行

這樣當你的網頁就會產生一串 token

如果還是不記得的話可以參考土豆大大的 slides

特此感謝土豆的超前部署 (?

  繼續寫程式吧

修改 view.py

def login(request):
    if request.user.is_authenticated:
        return redirect('/home/')
    username = request.POST.get('username', '')
    password = request.POST.get('password', '')
    user = auth.authenticate(username=username, password=password)
    if user is not None and user.is_active:
        auth.login(request, user)
        return redirect('/home/')
    else:
        return render(request, 'login.html', locals())

路徑:mysite / trips / view.py

Create Logout page

修改 view.py

def logout(request):
    auth.logout(request)
    return redirect('/home/')

路徑:mysite / trips / view.py

Create Registration page

新建 register.html

{% extends 'base.html' %}

{% block title %}
  <h1>Register Page</h1>
{% endblock title %}

{% block content %}
  <form method="POST">
      {% csrf_token %}
      {{ form.as_p }}
      {% for field in form %}
        {{ field.errors }}
      {% endfor %}
  <button type="submit">送出</button>
  </form>
{% endblock content %}

路徑:mysite / templates / registration / register.html

修改 view.py

from django.contrib.auth.forms import UserCreationForm

def register(request):
 if request.method == 'POST':
  form = UserCreationForm(request.POST)
  print("Errors", form.errors)
  if form.is_valid():
   form.save()
   return redirect('/home/')
  else:
    return render(request, 'registration/register.html', {'form':form})
 else:
  form = UserCreationForm()
  context = {'form': form}
  return render(request, 'registration/register.html', context)

路徑:mysite / trips / view.py

修改 urls.py

# 新增 register
from trips.views import home, post_detail..., register

urlpatterns = [
	...
    path('register/', register, name='register'), # 新增
]

路徑:mysite / urls.py

修改 settings.py

# 新增
LOGIN_REDIRECT_URL = '/home/'
LOGOUT_REDIRECT_URL = '/home/'

路徑:mysite / mysite / settings.py

Modify Basic page

修改 base.html

<nav class="navbar">
 <a class="navbar-brand" href="/home">My blog</a>
 <a class="navbar-brand" href="/showImg">My Image</a>
 ...
 <!--修改-->
 {% if request.user.is_superuser %}
  <a class="navbar-brand" href="/admin">Admin</a>
  <a class="navbar-brand" href="{% url 'logout' %}">Logout</a>
 {% elif request.user.is_authenticated %}
  <a class="navbar-brand" href="{% url 'logout' %}">Logout</a>
 {% else %}
  <a class="navbar-brand" href="{% url 'login' %}">Login</a>
  <a class="navbar-brand" href="/register">Sign up</a>
 {% endif %}
 <!--到這邊-->
</nav>

路徑:mysite / templates / base.html

所以              剛剛打了些什麼

{% if request.user.is_superuser %}
{% elif request.user.is_authenticated %}

.is_superuser

用戶是否認證過,若有回傳True

若為AnonymousUser物件,則回傳False

.is_authenticated

用戶是否為超級使用者

{% if request.user.is_superuser %}
  <a class="navbar-brand" href="/admin">Admin</a>
  <a class="navbar-brand" href="{% url 'logout' %}">Logout</a>
{% elif request.user.is_authenticated %}
  <a class="navbar-brand" href="{% url 'logout' %}">Logout</a>
{% else %}
  <a class="navbar-brand" href="{% url 'login' %}">Login</a>
  <a class="navbar-brand" href="/register">Sign up</a>
{% endif %}

補充一下 User 物件中的  Attribute

補充一下 User 物件中的  Method

Modify Home page

修改 home.html

{% block content %}
 <h2>Hello! this is my blog</h2>
 <!--修改 user.is_authenticated的部分-->
 {% if request.user.is_superuser %}
  <p>Welcome, {{ user.username }}</p>
  <a href="{% url 'post_new' %}"><span> 新增文章 </span></a>
 {% elif request.user.is_authenticated %}
  <p>Welcome, {{ user.username }}</p>
  ...
  {% empty %}
   <p>敬請期待</p>
  {% endfor %}
  </div>
 {% else %}
  <p>登入後即可查看文章</p>
 {% endif %}
{% endblock %}

路徑:mysite / templates / home.html

寫好了就來測試一下吧

$ python manage.py runserver

如果你有寫 shell 的話

$ runserver.sh

Update to Heroku

登入 Heroku

$ Heroku login

上傳檔案

$ git add .
$ git commit -m "你想留下的訊息"
$ git push heroku master

請確定自己進到要上傳的資料夾裡了

確定一下 Heroku 上的網頁能運作

$ heroku open

出現 Not Found 的時候,記得確認一下網址是否加了 /home

恭喜你完成           登入登出跟註冊了

Social Login - Github

安裝 social-auth-app-django

$ pip install social-auth-app-django

修改 requirements.txt

...
social-auth-app-django==3.4.0 # 新增

修改 login.html

<!--新增超連結-->
<a href="{% url 'social:begin' 'github' %}">Github 登入</a>

路徑:mysite / templates / registration / login.html

修改 urls.py

urlpatterns = [
 ...
 # 新增
 path('', include('social_django.urls', namespace='social')),
]

路徑:mysite / urls.py

修改 settings.py

INSTALLED_APPS = [
    ...
    # 新增
    'social_django',
]

路徑:mysite / mysite / settings.py

繼續修改 settings.py

TEMPLATES = [
 {
  'BACKEND': 'django.template.backends.django.DjangoTemplates',
  'DIRS': [os.path.join(BASE_DIR, 'templates')],
  'APP_DIRS': True,
  'OPTIONS': {
   'context_processors': [
    'django.template.context_processors.debug',
    'django.template.context_processors.request',
    'django.contrib.auth.context_processors.auth',
    'django.contrib.messages.context_processors.messages',
    'social_django.context_processors.backends', # 新增
    'social_django.context_processors.login_redirect', #新增
   ],
  },
 },
]

路徑:mysite / mysite / settings.py

還是在修改 settings.py

# 新增
AUTHENTICATION_BACKENDS = (
    'social_core.backends.github.GithubOAuth2',
    'django.contrib.auth.backends.ModelBackend',
)

SOCIAL_AUTH_URL_NAMESPACE = 'social'
SOCIAL_AUTH_GITHUB_USE_OPENID_AS_USERNAME = True

# 登入成功後轉址
SOCIAL_AUTH_LOGIN_REDIRECT_URL = '/home'

路徑:mysite / mysite / settings.py

Github OAuth

先去申請開放授權(OAuth)

https://github.com/settings/applications/new

OAuth 簡介

Open  Authorization


透過這種協定,使用者可以在不透露帳號密碼的情況下,

授權第三方網路應用服務使用(或登入)原本的網路服務

申請好後就能獲得 Client ID 和 Client Secret

修改 settings.py

# 新增
SOCIAL_AUTH_GITHUB_KEY = '你的 Client ID'
SOCIAL_AUTH_GITHUB_SECRET = '你的 Client Secret'

路徑:mysite / mysite / settings.py

同步一下資料庫

$ cd 到 manage.py 所在位置
$ python manage.py migrate

本地端測試

$ python manage.py runserver

如果你有寫 shell 的話

$ runserver.sh

Lab - Google Login

Modify CSS

 自由發揮時間

(因為我覺得內建有點醜

(這是為了沒 bug 的人找的工作

Update to Heroku

如果你還沒登入 Heroku 的話

$ Heroku login

上傳檔案

$ git add .
$ git commit -m "你想留下的訊息"
$ git push heroku master

請確定自己進到要上傳的資料夾裡了

接著初始化資料庫

$ heroku run python mysite/manage.py migrate

放上去的話記得要改這邊的網址呦

確定一下 Heroku 上的網頁能運作

$ heroku open

出現 Not Found 的時候,記得確認一下網址是否加了 /home

Thanks for listening.

References

Django - User

By oneone

Django - User

SIRLA 2020 Django Tutorial

  • 143