DjangoでSNS(1/2):ユーザー認証編
後編はこちら
長いので前後半に分割した。
- 前編:ユーザー認証
- 後編:SNS本体(CRUD操作)
後編はこちら。
はじめに
最近Djangoの勉強に取り組んでいる。CRUD操作を学ぶためにTodoリストをつくったり、ユーザー認証を試すためにログインとログアウトくらいしかできないアプリをつくったりした(まだスクラップだが...)。
今度はCRUD操作とユーザー認証を組み合わせて何かつくろうと考え、SNSに挑戦することにした。
つくるもの
- 関数ベースビューでつくる。クラスベースビューの方がシンプルに書けるが、処理を追いづらいというか...
- CSSとかBootStrapは使わない。デザインを捨てて、機能の実装に焦点を当てる。
トップ画面
- 最初の画面。
- ユーザー登録画面とログイン画面に進める。
ユーザー登録画面
- ユーザー名、メールアドレス、パスワード、パスワード(確認用)を入力して登録する。
- 登録に成功するとトップ画面に遷移する。
ログイン画面
- ログインする。
- ログインに成功するとタイムラインに遷移する。
タイムライン
- 全てのユーザーの投稿が更新日時順に表示される。
- 表示される情報は更新日時とユーザー名、本文。
- 投稿の本文をクリックすると、その投稿の詳細画面に遷移する。
- 投稿のユーザー名をクリックすると、そのユーザーだけの投稿一覧画面に遷移する。
ユーザーごとの投稿一覧画面
- タイムラインで選択したユーザーの投稿だけの一覧を表示する。
投稿詳細画面
- 詳細情報として作成日時、更新日時、ユーザー名、本文が表示される(詳細といってもタイムラインに表示している情報とほとんど同じだが...)。
- 自分で作成した投稿に限り、更新ボタンと削除ボタンが表示される。
投稿作成画面
- 投稿を新規作成する。
投稿更新画面
- 投稿を更新する。
投稿削除画面
- 投稿を削除してよいか確認する。
ログアウト画面
- ログアウトしたことを示す。
- トップ画面へのリンクを表示する。
初期設定
Django用のディレクトリ作成
- エクスプローラーで任意の場所にDjango用のディレクトリをつくる。ここではCドライブ直下に
django
というディレクトリをつくった。 - VSCodeを開く>
File
>Open Folder
>django
を選択してディレクトリを開く。
仮想環境作成(任意)
仮想環境をつくる。
C:\django> python -m venv venv
仮想環境を有効化する。有効化されるとコマンドラインの先頭に環境名が表示される。
C:\django> venv\Scripts\activate
(venv) C:\django>
Djangoインストール
(venv) C:\django> pip install django
プロジェクト作成
config
プロジェクトを作成する。
(venv) C:\django> django-admin startproject config .
アプリケーション作成
ユーザー認証を行うaccounts
アプリケーション、SNS本体となるsns
アプリケーションを作成する。
(venv) PS C:\django> python manage.py startapp accounts
(venv) PS C:\django> python manage.py startapp sns
設定ファイル編集
config\settings.py
を編集する。
先ほど作成したアプリケーションを登録する。
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
+ "accounts",
+ "sns",
]
言語を変更する。
- LANGUAGE_CODE = "en-us"
+ LANGUAGE_CODE = "ja"
タイムゾーンを変更する。
- TIME_ZONE = "UTC"
+ TIME_ZONE = "Asia/Tokyo"
動作確認
(venv) PS C:\django> python manage.py runserver
http://127.0.0.1:8000/
にアクセスすると、いつものロケットが表示される。
トップ画面
ユーザー認証の前にとりあえずトップ画面を作る。
View
sns\views.py
ファイルに以下を入力する。
from django.shortcuts import render
def top_view(request):
return render(request, "sns/top.html")
-
render()
でテンプレートを表示するだけである。
URL
config\urls.py
ファイルを以下のように編集する。
from django.contrib import admin
- from django.urls import path
+ from django.urls import include, path
urlpatterns = [
path("admin/", admin.site.urls),
+ path("", include("sns.urls")),
]
sns
ディレクトリにurls.py
ファイルをつくり、以下を入力する。
from django.urls import path
from . import views
app_name = "sns"
urlpatterns = [
path("top/", views.top_view, name="top"),
]
Template
sns
ディレクトリに以下のようにディレクトリとファイルをつくる。
django
├── configとか
└── sns
+ └── templates
+ └── sns
+ └── top.html
top.html
ファイルに以下を入力する。
<!DOCTYPE html>
<html lang='ja'>
<head>
<meta charset='UTF-8' />
<title>俺のSNS</title>
</head>
<body>
<p>俺のSNS</p>
<p>日常で思ったことなどをつぶやくSNSです</p>
<ul>
<li><a href="#undefined">ユーザー登録</a></li>
<li><a href="#undefined">ログイン</a></li>
</ul>
</body>
</html>
- ユーザー登録、ログインへのリンクはまだつくっていないので、仮で
#undefined
を指定した。以前の私は#
だけだったが、仮であることをより明確にするために#undefined
にした(参考)。
動作確認
http://127.0.0.1:8000/top/
にアクセスすると、以下のようにトップ画面が表示される。
ユーザー登録画面
Model(カスタムユーザーの定義)
DjangoにはデフォルトのUser
モデルが存在するが、一度マイグレーションするとfield
を追加できないらしい(参考:【Django】カスタムユーザーの作成)。そこでカスタムユーザーと呼ばれるUser
モデルを独自に定義する。
accounts\models.py
に以下を入力する。
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
pass
-
User
モデルと同じfield
を用いるなら、AbstractUser
クラスを継承するだけである。
認証に使用するモデルをデフォルトのユーザーからカスタムユーザーに変更する。
settings.py
に以下を追加する("アプリ名.モデル名"
)。
+ AUTH_USER_MODEL = "accounts.User"
↑あれを追加しないと、エラーが出る。
ERRORS:
accounts.User.groups: (fields.E304) Reverse accessor 'Group.user_set' for 'accounts.User.groups' clashes with reverse accessor for 'auth.User.groups'.
HINT: Add or change a related_name argument to the definition for 'accounts.User.groups' or 'auth.User.groups'.
accounts.User.user_permissions: (fields.E304) Reverse accessor 'Permission.user_set' for 'accounts.User.user_permissions' clashes with reverse accessor for 'auth.User.user_permissions'.
HINT: Add or change a related_name argument to the definition for 'accounts.User.user_permissions' or 'auth.User.user_permissions'.
auth.User.groups: (fields.E304) Reverse accessor 'Group.user_set' for 'auth.User.groups' clashes with reverse accessor for 'accounts.User.groups'.
HINT: Add or change a related_name argument to the definition for 'auth.User.groups' or 'accounts.User.groups'.
auth.User.user_permissions: (fields.E304) Reverse accessor 'Permission.user_set' for 'auth.User.user_permissions' clashes with reverse accessor for 'accounts.User.user_permissions'.
HINT: Add or change a related_name argument to the definition for 'auth.User.user_permissions' or 'accounts.User.user_permissions'.
モデルを変更したのでマイグレーションを行う。
(venv) C:\django> python manage.py makemigrations
(venv) C:\django> python manage.py migrate
Form
accounts
ディレクトリにforms.py
ファイルを作成し、以下を入力する。
from django.contrib.auth.forms import UserCreationForm
from .models import User
class SignUpForm(UserCreationForm):
class Meta:
model = User
fields = ("username", "email", "password1", "password2")
-
fields
で指定した項目がユーザー登録画面に入力項目として表示される。
View
accounts\views.py
に以下を入力する。
from django.shortcuts import render, redirect
from .forms import SignUpForm
def signup_view(request):
if request.method == "POST":
form = SignUpForm(request.POST)
if form.is_valid():
form.save()
return redirect("sns:top")
else:
form = SignUpForm()
param = {"form": form}
return render(request, "accounts/signup.html", param)
URL
config\urls.py
ファイルを以下のように編集する。
urlpatterns = [
path("admin/", admin.site.urls),
path("", include("sns.urls")),
+ path("accounts/", include("accounts.urls")),
]
accounts
ディレクトリにurls.py
ファイルをつくり、以下を入力する。
from django.urls import path
from . import views
app_name = "accounts"
urlpatterns = [
path("signup/", views.signup_view, name="signup"),
]
Template
accounts
ディレクトリに以下のようにディレクトリとファイルをつくる。
django
├── configとか
└── accounts
+ └── templates
+ └── accounts
+ └── signup.html
signup.html
ファイルに以下を入力する。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>ユーザー登録</title>
</head>
<body>
<h1>ユーザー登録</h1>
<form action="{% url 'accounts:signup' %}" method="post">
{% csrf_token %} {{ form.as_p }}
<button type="button" onclick="location.href=`{% url 'sns:top' %}`">
戻る
</button>
<input type="submit" value="登録" />
</form>
</body>
</html>
-
<form>
のaction
属性はフォームデータの送信先である。ここに{% url 'accounts:signup' %}
を指定することで、Viewに入力値を送信できる。 -
forms.py
で指定したfields
の入力フォームが{{ form }}
に表示される。.as_p
を付けることで各入力項目が<p>
タグで囲まれる(無くてもいいが、見やすくなる)。 - 戻るボタンには
type="button"
を指定した。デフォルトではtype="submit"
なので、省略するとフォームを送信してしまう(参考)。 - ボタン要素にDjangoのURLを指定するには、「onclick="location.href=`{% url 'app_name:hoge' %}`"」と長々しく記述しなければならない。
sns\templates\sns\top.html
ファイルを以下のように変更する。
- <li><a href="#undefined">ユーザー登録</a></li>
+ <li><a href="{% url 'accounts:signup' %}">ユーザー登録</a></li>
- トップ画面にあるユーザー登録画面へのリンクを有効にした。
動作確認
http://127.0.0.1:8000/accounts/signup/
にアクセスすると、以下のようにユーザー登録画面が表示される。
後でSNSの動作確認に使うので、ユーザーを2つ登録する。パスワードは忘れないように。
項目 | 値 |
---|---|
ユーザー名 | tanaka |
メールアドレス | tanaka@example.com |
パスワード | **********(お好みで) |
項目 | 値 |
---|---|
ユーザー名 | yamada |
メールアドレス | yamada@example.com |
パスワード | **********(お好みで) |
登録ボタンを押してエラーがなければトップ画面に遷移する。
ログイン画面
Form
accounts\forms.py
ファイルを以下のように編集する(⋮
は省略を示す)。
- from django.contrib.auth.forms import UserCreationForm
+ from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
⋮
+ class LoginForm(AuthenticationForm):
+ pass
-
AuthenticationForm
クラスを継承するだけ。
View
accounts\views.py
を以下のように編集する。
from django.contrib.auth import login
from django.shortcuts import redirect, render
from .forms import LoginForm, SignUpForm
⋮
def login_view(request):
if request.method == "POST":
form = LoginForm(request, data=request.POST)
if form.is_valid():
user = form.get_user()
if user:
login(request, user)
return redirect(to="/accounts/login/#undefined/")
else:
form = LoginForm()
param = {"form": form}
return render(request, "accounts/login.html", param)
- ログイン成功後のリダイレクト先は仮である。
URL
urls.py
ファイルを以下のように編集する。
urlpatterns = [
path("signup/", views.signup_view, name="signup"),
+ path("login/", views.login_view, name="login"),
]
Template
accounts\templates\accounts
ディレクトリにlogin.html
ファイルをつくり、以下を入力する。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>ログイン</title>
</head>
<body>
<h1>ログイン</h1>
<form action="{% url 'accounts:login' %}" method="post">
{% csrf_token %} {{ form.as_p }}
<button type="button" onclick="location.href=`{% url 'sns:top' %}`">
戻る
</button>
<input type="submit" value="ログイン" />
</form>
</body>
</html>
sns\templates\sns\top.html
ファイルを以下のように変更する。
- <li><a href="#undefined">ログイン</a></li>
+ <li><a href="{% url 'accounts:login' %}">ログイン</a></li>
トップ画面にあるログイン画面へのリンクを有効にした。
動作確認
http://127.0.0.1:8000/accounts/login/
にアクセスすると、以下のようにログイン画面が表示される。
先ほど登録したユーザー名とパスワードを入力してログインボタンを押す。ログイン先の画面をまだつくっていないのでボタンを押しても変化はないが、URLがhttp://127.0.0.1:8000/accounts/login/#undefined/
になっていれば成功。
ログアウト画面
View
views.py
ファイルを以下のように編集する。
from django.contrib.auth import login, logout
from django.shortcuts import redirect, render
from .forms import LoginForm, SignUpForm
⋮
def logout_view(request):
logout(request)
return render(request, "accounts/logout.html")
-
logout()
だけでログアウトできる。
URL
urls.py
ファイルを以下のように編集する。
urlpatterns = [
path("signup/", views.signup_view, name="signup"),
path("login/", views.login_view, name="login"),
+ path("logout/", views.logout_view, name="logout"),
]
Template
accounts\templates\accounts
ディレクトリにlogout.html
ファイルをつくり、以下を入力する。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>ログアウト</title>
</head>
<body>
<p>ログアウトしました</p>
<a href="{% url 'sns:top' %}">トップへ</a>
</body>
</html>
- ログアウトしたことを示すとともにトップ画面へのリンクを表示するだけ。
動作確認
http://127.0.0.1:8000/accounts/logout/
にアクセスすると、以下のようにログアウト画面が表示される。
認証の作成は一旦完了である。次の記事でSNS機能を完成させた後、SNSに認証を紐づける。
Github
TODO:コードアップロードする
Discussion