![見出し画像](https://assets.st-note.com/production/uploads/images/55076540/rectangle_large_type_2_8eb4bbbe28d091210762761243c5df48.png?width=1200)
djangoで作る本格的なSNSアプリケーション 番外編 allauthによる認証
djangoで簡単なTodoアプリ等は作成できたけれども、オリジナルWebアプリを作る前にもう少しだけ本格的なアプリをチュートリアル形式で取り組みたい方を対象としています。
Part1、Part2で作成したaccountsアプリをDjango-allauthで実装してみたいと思います。allauthを使うとユーザー認証機能がとても簡単に実装できますので、どれほど簡単になるのかの比較にもなるかと思います。
ソースコードは、Githubに載せておりますのでそちらも参照ください。
目標
django-allauthを実装し、SNSアプリのaccountsアプリを作成出来るようになります。クラスベース汎用ビューを前提として開発を進めます。
開発環境、前提条件、IDEの設定
Part1と同じほぼ環境と前提で開発していきますので、そちらをご覧ください(IDEはPycharmを使用しています)。
仮想環境と必要なパッケージ
・django_envという仮想環境を作成し
・django3.2.4
・Pillow
・django-bootstrap4
・bcrypt
・beautifulsoup4
・django-allauth(0.41.0) ⇦ 今回追加
開発の手順のおさらい
①sns2プロジェクトの開始とアプリの作成
②ユーザーモデルとユーザー登録機能 ← Allauthで実装
③ログイン、ログアウト機能 ← Allauthで実装
④ユーザー情報の更新と一覧表示 ← Allauthで実装
⑤ポストモデルとCRUD操作
⑥ユーザのFollow/Unfollow機能の追加
⑦ポストのお気に入り機能追加
SNS2プロジェクトの開始と初期設定
プロジェクト名をallauth有無で分けるためsns2とします。sns2プロジェクトを開始し、accountsアプリを作成します。まずは、sns2プロジェクトを保存するフォルダーにターミナルで移動します。今回は、django_projectsというフォルダー内にsns2プロジェクトを作成します。
python manage.py startapp accounts
これから必要となるフォルダーやファイルを最初に作ります。画像を確認して下さい(VisualStudioCodeではなく、Pycharmの画面になりますがご了承ください)。特に気をつけていただきたいのは、templates内のaccountフォルダです。accountsではなく、accountです。allauthのlogin.htmlなどを上書きする予定ですので、わざとaccountにしています。
![画像1](https://assets.st-note.com/production/uploads/images/55132454/picture_pc_0930ce967f25f318a1a73acb59f02eec.png)
設定ファイル(settings.py)の編集
これまでに作成したアプリ、パッケージ、フォルダーなどが使えるようにsettings.pyを変更していきます。settings.pyのコードは以下のようになります。
sns2/settings.py
from pathlib import Path
import os # 追加
from django.contrib.messages import constants as messages # 追加
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-89v+92f@u!49l7&4nd8k=)h7iqz$2$bloy%c=iu2j!xe46f'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'accounts', # 追加
'bootstrap4', # 追加
'django.contrib.sites', # 追加
'allauth', # 追加
'allauth.account', # 追加
'allauth.socialaccount', # 追加
]
SITE_ID = 1
LOGIN_REDIRECT_URL = '/accounts/home'
ACCOUNT_LOGOUT_REDIRECT_URL = 'account_login'
ACCOUNT_EMAIL_VERIFICATION = 'none'
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'sns2.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'] # VSCodeの場合は変更
,
'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',
],
# Bootstrap4を使用するために追加
'builtins': [
'bootstrap4.templatetags.bootstrap4', ]
},
},
]
WSGI_APPLICATION = 'sns2.wsgi.application'
# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/
LANGUAGE_CODE = 'ja' # 変更
TIME_ZONE = 'Asia/Tokyo' # 変更
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/
STATIC_URL = '/static/'
# 追加
STATICFILES_DIRS = (
os.path.join(BASE_DIR, "static"),
)
# 追加 mediaを扱うための設定
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# 追加
IMAGE_URL = '/images/'
IMAGE_ROOT = os.path.join(BASE_DIR, 'images')
# 追加 メッセージタグの設定
MESSAGE_TAGS = {
messages.ERROR: 'alert alert-danger',
messages.WARNING: 'alert alert-warning',
messages.SUCCESS: 'alert alert-success',
messages.INFO: 'alert alert-info'
}
# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# 一番下に追加
AUTH_USER_MODEL = 'accounts.CustomUser'
ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_USER_MODEL_USERNAME_FIELD = None
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_LOGOUT_ON_GET = True、設定も実施してしまいます。
allauthを使用するために、INSTALLE _APPSに3つ追加しています。また、SITE _ID = 1はallauthを使用する際の決まりであり、それ以下はURLの認証等に必要ですので追加して下さい。
INSTALLED_APPS = [
・・・省略・・・
'allauth', # 追加
'allauth.account', # 追加
'allauth.socialaccount', # 追加
]
SITE_ID = 1 # 追加
LOGIN_REDIRECT_URL = '/accounts/home' # 追加
ACCOUNT_LOGOUT_REDIRECT_URL = 'account_login' # 追加
ACCOUNT_EMAIL_VERIFICATION = 'none' # 追加
後ほどプロフィール画像を登録できるようにしていきますが、プロフィール画像はimagesフォルダーへ保存するようにしますので、その設定も追記していきましょう。
IMAGE_URL = '/images/'
IMAGE_ROOT = os.path.join(BASE_DIR, 'images')
また、ユーザーモデルをCustomUserとして作成しますので、ユーザーモデルを設定します。ACCOUNT_AUTHENTICATION_METHOD = 'email'では、認証方法をusernameではなくemailとして設定します。また、usernameは今回使用しませんので不要とします。
# 一番下に追加
AUTH_USER_MODEL = 'accounts.CustomUser' # 追加
ACCOUNT_AUTHENTICATION_METHOD = 'email' # 追加
ACCOUNT_USER_MODEL_USERNAME_FIELD = None # 追加
ACCOUNT_EMAIL_REQUIRED = True # 追加
ACCOUNT_USERNAME_REQUIRED = False # 追加
ACCOUNT_LOGOUT_ON_GET = True
sns2/urls.pyの設定ですが、mediaやStaticフォルダを使う設定飲み実施します。まだ、この段階ではaccountsやallauthへパスを通さないで下さい。マイグレーション事にエラーが出ます。
from django.contrib import admin
from django.urls import path, include
from django.conf.urls.static import static # mediaを使うために追加
from . import settings # mediaを使うために追加
urlpatterns = [
path('admin/', admin.site.urls),
]
if settings.DEBUG:
urlpatterns += static(settings.IMAGE_URL, document_root=settings.IMAGE_ROOT) # 追加
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) # 追加
これで最初の設定が終わりましたので、次はカスタムユーザーを作成します。
CustomUserモデル作成とマイグレーション
これからカスタムユーザーを作ってマイグレーションを実施します。accounts/ models.pyを開いて下さい。最終的なコードは以下になります。
accounts/models.py
from django.db import models
from django.contrib.auth.base_user import AbstractBaseUser
from django.contrib.auth.models import UserManager, PermissionsMixin
# Create your models here.
class UserManager(UserManager):
def _create_user(self, email, password, **extra_fields):
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_user(self, email, password=None, **extra_fields):
extra_fields.setdefault('is_staff', False)
extra_fields.setdefault('is_superuser', False)
return self._create_user(email, password, **extra_fields)
def create_superuser(self, email, password, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
if extra_fields.get('is_staff') is not True:
raise ValueError('Superuser must have is_staff=True.')
if extra_fields.get('is_superuser') is not True:
raise ValueError('Superuser must have is_superuser=True.')
return self._create_user(email, password, **extra_fields)
class CustomUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField('メールアドレス', unique=True)
nickname = models.CharField('ニックネーム', max_length=50)
# プロフィール画像をavatarとして設定
avatar = models.ImageField(upload_to='images', verbose_name='プロフィール画像', blank=True, null=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'email'
EMAIL_FIELD = 'email'
REQUIRED_FIELDS = []
class Meta:
verbose_name = ('user')
verbose_name_plural = ('users')
def clean(self):
super().clean()
self.email = self.__class__.objects.normalize_email(self.email)
CustomUserモデルには、usernameを使用しない代わりにnicknameを設定します。また、プロフィール画像をmedia/imagesフォルダ内にアップロードするため、avatar = models.ImageField(upload_to='images', verbose_name='プロフィール画像', blank=True, null=True)とします。また、emailをユーザーを識別する唯一の情報としますのでUSERNAME_FIELD = 'email'とします。
ユーザーモデルが作成できましたので、マイグレーションをおこないます。以下の画像のようにデータベースが作成されていれば成功です。もし、マイグレーションでエラーが発生してしまった場合は、これ以前に一度マイグレーションしていないか思い出してみて下さい。モデル定義前にマイグレーションをかけてしまうと解決が難しいので、素直にもう一度プロジェクトを作成し直した方が時短になります。
python manage.py makemigrates
python manage.py migrate
![画像2](https://assets.st-note.com/production/uploads/images/55134727/picture_pc_2091c38dd7ccf5b2ed93186b52bb6e0e.png?width=1200)
allauthによる認証機能の実装
モデルが完成しましたので、通常であればviews.pyやforms.pyを作成するのですが、allauthがログイン、ログアウト、サインイン等の基本的な機能を備えています。このため、特別な事をしないのであれば改めて作成する必要はありません。することは、パスを通して見た目すだけになります。allauthを使えるようにするために、sns2/urls.pyとaccounts/urls.pyにパスを通します。
sns2/urls.pyにはallauthとaccountsのurlsが使えるように設定します。allauthのurlと言われてもピンと来ないと思いますがsite-packages/allauth以下にurls.pyが保存されています。そこで基本的なパスは既に設定させています。
sns2/urls.py
from django.contrib import admin
from django.urls import path, include
from django.conf.urls.static import static # mediaを使うために追加
from . import settings # mediaを使うために追加
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include('allauth.urls')), # 追加
path('accounts/', include('accounts.urls')), # 追加
]
if settings.DEBUG:
urlpatterns += static(settings.IMAGE_URL, document_root=settings.IMAGE_ROOT)
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
![画像3](https://assets.st-note.com/production/uploads/images/55135721/picture_pc_7125ba938c2b56bc0512678657e226be.png?width=1200)
accounts/urls.pyにもパスを通します。特になくても困らないですが、トップページとしてHomeViewを作成しました。
accounts/urls.py
from django.urls import path
from .views import HomeView
app_name = 'accounts'
urlpatterns = [
path('home/', HomeView.as_view(), name='home'),
]
allauthへ自作テンプレートを適用
allauthはテンプレートも準備してくれています。しかし、そのままでは以下のような味気ないテンプレートになってしまいます。自分好みのテンプレートを作成して、allauthの標準テンプレートを上書きしていきます。
![ログインallauth](https://assets.st-note.com/production/uploads/images/55136228/picture_pc_5c8ffbcf24efb6fdf45aca547f7d7029.png?width=1200)
![サインアップallauth](https://assets.st-note.com/production/uploads/images/55136236/picture_pc_8b8658d86e816cc92e9952b04272f81c.png?width=1200)
まずは、出来上がりイメージを掴んで下さい。bootstrapを適用しています。djangoで作る本格的なSNSアプリケーションPar 1,Part2とほぼ同じ見た目になります。usernameが不要になった事、ユーザー登録時はパスワードを2回入力することになった事以外はほぼ同じです。
![スクリーンショット 2021-06-21 午後20.55.53 午後](https://assets.st-note.com/production/uploads/images/55136485/picture_pc_be561019c5e78122545b49fa88d8d443.png?width=1200)
![スクリーンショット 2021-06-21 午後20.55.59 午後](https://assets.st-note.com/production/uploads/images/55136488/picture_pc_736b6fa45539067be6368164d2fadc92.png?width=1200)
まずは、base.htmlを作成します。Part1, Part2の時からの使い回しですので変更を加えた部分のみ説明します。
templates/base.html
{% load static %}
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Microposts</title>
{# bootstrapのCSS、自作のCSSを読み込む#}
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link rel="stylesheet" href="{% static 'style.css' %}">
{% load bootstrap4 %}
</head>
<body>
<header class="mb-4">
<nav class="navbar navbar-expand-sm navbar-dark bg-dark">
<a class="navbar-brand" href="#">Microposts</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav navbar-left">{% if user.is_authenticated %}
<li class="nav-item">
<a class="nav-link" href="#">All Posts</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">All Users</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">New post</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" role="button"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ user.username }}'s detail
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
<a class="dropdown-item" href="#">My posts</a>
<a class="dropdown-item" href="#">Following</a>
<a class="dropdown-item" href="#">Follower</a>
</div>
</li>{% endif %}
</ul>
<ul class="navbar-nav navbar-right">{% if user.is_authenticated %}
<li class="nav-item">{# {% url 'account_logout' %}は、Allauthの定型文 #}
<a class="nav-link" href="{% url 'account_logout' %}">Logout</a>
</li>{% else %}
<li class="nav-item">{# {% url 'account_login' %}は、Allauthの定型文 #}
<a class="nav-link" href="{% url 'account_login' %}">Login</a>
</li>
<li class="nav-item">{# {% url 'account_signup' %}は、Allauthの定型文 #}
<a class="nav-link" href="{% url 'account_signup' %}">Sign in</a>
</li>{% endif %}
</ul>
</div>
</nav>
</header>
{% block content %}
<!-- ここに各htmlの内容が反映される -->
{% endblock content %}
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS,Fontawasome -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
crossorigin="anonymous"></script>
<script defer src="https://use.fontawesome.com/releases/v5.7.2/js/all.js"></script>
{% block footer %}
{% endblock footer %}
</body>
</html>
navbarのhref属性リンクですが、allauthのurlを使用しますのでログアウト、ログイン、ユーザー登録はそれぞれhref="{% url 'account_logout' %}"、href="{% url 'account_login' %}"、href="{% url 'account_signup' %}"となります。下の画像は、allauth/account/urls.pyのコードです。こちらでも、なぜそうなっているか確認できると思います。
<ul class="navbar-nav navbar-right">{% if user.is_authenticated %}
<li class="nav-item">{# {% url 'account_logout' %}は、Allauthの定型文 #}
<a class="nav-link" href="{% url 'account_logout' %}">Logout</a>
</li>{% else %}
<li class="nav-item">{# {% url 'account_login' %}は、Allauthの定型文 #}
<a class="nav-link" href="{% url 'account_login' %}">Login</a>
</li>
<li class="nav-item">{# {% url 'account_signup' %}は、Allauthの定型文 #}
<a class="nav-link" href="{% url 'account_signup' %}">Sign in</a>
</li>{% endif %}
</ul>
![画像8](https://assets.st-note.com/production/uploads/images/55136897/picture_pc_8edcf8a555bb36db61803c5f241332c4.png?width=1200)
ログイン画面、ユーザー登録画面にもbootstrapを適用していきます。テンプレートを保存するフォルダをtemplate/accountにしましたが、allauthの標準テンプレートを上書きするために設定しました。フォルダ名だけ間違えなければ、シンプルなページですのでコードのみ載せます。
templates/account/login.html
{% extends 'base.html' %}
{% block content %}
<div class="row">
<div class="col-sm-6 offset-sm-3">
<h1 >ログイン</h1>
{# ここ{% url 'account_login' %}は、Allauthを使う際の定型文#}
<form method="POST" action="{% url 'account_login' %}">
{% csrf_token %}
{% bootstrap_form form %}
<button type="submit" class="btn btn-primary" name="button">
ログイン
</button>
</form>
</div>
</div>
{% endblock %}
templates/account/signup.html
{% extends 'base.html' %}
{% block content %}
<div class="row">
<div class="col-sm-6 offset-sm-3">
<h1 class="mb-4">ユーザー登録</h1>
{# ここ{% url 'account_signup' %}は、Allauthを使う際の定型文#}
<form method="POST" action="{% url 'account_signup' %}">
{% csrf_token %}
{% bootstrap_form form %}
<button type="submit" class="btn btn-primary" name="button">
登録
</button>
</form>
</div>
</div>
{% endblock %}
トップページを作成します。Homeviewと対応するhome.htmlを作成します。それぞれ、とてもシンプルですのでコードのみ載せます。
accounts/views.py
from django.shortcuts import render
from django.views.generic.base import TemplateView
# Create your views here.
class HomeView(TemplateView):
template_name = 'account/home.html'
templates/account/home.html
{% extends 'base.html' %}
{% load bootstrap4 %}
{% block content %}
<div class="center jumbotron">
<div class="text-center">
<h1>Welcome to the Microposts</h1>
{# ここ{% url 'account_signup' %}は、Allauthを使う際の定型文#}
<a class="btn btn-outline-secondary btn-sm"
href="{% url 'account_signup' %}"
role="button">Sign in Now!</a>
</div>
</div>
{% endblock %}
たったこれだけでallauthのログイン、ログアウト、サインアップの基本的な認証機能実装は完了です。拍子抜けするくらい簡単です。コードの量もallauthを使わない場合に比べて半分以下で済みます。
ターミナルを立ち上げて動作を確認して見て下さい。自作のテンプレートはうまく表示されているでしょうか?ユーザーを登録してみて、データベースに反映されるかなども確認してみて下さい。
ユーザープロフィール更新の実装
ユーザープロフィールについては、allauthで準備されていませんので自分で作成する必要があります。プロフィール更新ですので、CRUDのUpdateということでクラスベース汎用ビューのUpdateViewを使っていきます。accounts/views.pyを開いて下さい。
コードの前半で必要なモジュールをインポートします。UpdateView、LoginRequiredMixin及びプロフィール更新に必要なフォームProfileForm(後ほど作成します)をインポートします。ユーザープロフィール画面のテンプレートは、edit_profile.htmlとし、使用するモデルはCustomUser、使用するフォームはProfileFormになります。更新完了後は、success_url= '/accounts /edit_profile/'として同じページを再度読み込みます。
accounts/views.py
from django.views.generic.base import TemplateView
from django.views.generic.edit import UpdateView # 追加
from django.contrib.auth.mixins import LoginRequiredMixin # 追加
from .models import CustomUser
from .forms import ProfileForm # 追加
class HomeView(TemplateView):
template_name = 'account/home.html'
class ProfileEditView(LoginRequiredMixin, UpdateView): # 追加
template_name = 'account/edit_profile.html'
model = CustomUser
form_class = ProfileForm
success_url = '/accounts/edit_profile/'
def get_object(self):
return self.request.user
次は、ProfileForm をforms.pyに設定します。ProfileFormはforms.ModelFormを継承して作成します。def __init__(self, *args, **kwargs):では、bootstrapのform-controlが出来るように設定します。次に、Class Metaとして、フォームのフィールドを設定します。使用するモデルは、CustomUser、ユーザー情報として更新したいフィールドをnickname, email, avatarとします。help_texts ={ }では、それぞれのフィールドの補足を加えられます。ここでは、nicknameの補足としてユーザーネームと表示されるようにしています。好みで設定を加えて見て下さい。
accounts/forms.py
from django import forms
from .models import CustomUser
class ProfileForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ProfileForm, self).__init__(*args, **kwargs)
for field in self.fields.values(): # bootstrapで使用するform-controlクラス
field.widget.attrs['class'] = 'form-control'
class Meta:
model = CustomUser
fields = ('nickname', 'email', 'avatar')
help_texts = {
'nickname': "ユーザーネーム",
'email': None,
}
urls.pyでパスを通します。名前空間は、name='edit_profile'と設定します。
accounts/urls.py
from django.urls import path
from .views import HomeView, ProfileEditView # 追加
app_name = 'accounts'
urlpatterns = [
path('home/', HomeView.as_view(), name='home'),
path('edit_profile/', ProfileEditView.as_view(), name='edit_profile'), # 追加
]
対応するテンプレートを作成します。こちらも、他のテンプレートとあまり変わりません。画像ファイルをアップロードするためには、<form>タグ内で、enctype="multipart/form-data"を指定しなければいけません。これを設定しないと、画像がアップロード出来ません。ご興味があれば、enctypeを外して試して見て下さい。
account/edit_profile.html
{% extends 'base.html' %}
{% block content %}
<div class="content-wrapper">
<div class="container-fluid">
<div class="row">
<div class="col-sm-6 offset-sm-3">
<div class="card">
<div class="card-header">
<h4><b>プロフィール更新</b></h4>
</div>
<div class="card-body">
{# 画像ファイルをアップロードするためenctype="multipart/form-data"を指定#}
<form action="{% url 'accounts:edit_profile' %}" method="post"
enctype="multipart/form-data">
{% csrf_token %}
{% bootstrap_form form %}
<button class='btn btn-outline-success btn-block' type="submit">更新</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
ターミナルからサーバーを立ち上げhttp://127.0.0.1:8000/accounts/ edit_profile/へアクセスして動作を確認して見て下さい。LoginRequiredMixinを設定していますので、ログインしていない状態ではアクセスできないと思います。下の画像のように、ニックネームや画像登録などが出来ましたでしょうか?
![プロフィール更新](https://assets.st-note.com/production/uploads/images/55196101/picture_pc_9ea69ab2fa30d91b26366d9a8e803124.png?width=1200)
![プロフィール更新2](https://assets.st-note.com/production/uploads/images/55196153/picture_pc_64dfdd31c02154aee4c3c3d4a4d96e77.png?width=1200)
このようにallauthを用いると非常に簡単に基本的な認証を実装できます。また、googleやFaceBookなどのSNS認証にも対応していますので、SNS認証をdjangoに実装したい場合にもallauthは有効です。
ソースコードは、Githubに載せておりますのでそちらも参照ください。