Django 管理サイト管理者以外のユーザー認証を実装する
2020/10/21 (更新:2020/11/19)
概要
Django には管理サイトの管理者ユーザー機能はありますが、管理サイトを利用しない一般ユーザーの機能はありません。
Django のユーザー機能を利用し、一般ユーザーの標準機能を実装します。
今後のシステムへの流用を考慮し、自前処理は必要最低限に抑えシンプル構成で実装します。
リポジトリです。
github.com/tassk-work/django-accounts-sample
一般ユーザーの標準機能
- ユーザー登録
- ログイン
- ログアウト
- パスワード変更
プロジェクト構成
- configシステム設定
- accounts一般ユーザー機能
- sample主アプリケーション
コード
システム設定
デフォルトとの差異のみ引用します。
config/settings.py
INSTALLED_APPS = [
...
'accounts',
'sample',
]
TEMPLATES = [
{
'DIRS': [os.path.join(BASE_DIR, 'templates')],
...
},
]
MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'
システムURL
config/urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include('accounts.urls')),
path('', include('sample.urls')),
]
一般ユーザー機能
一般ユーザー機能URL
ログインloginと、ログアウトlogoutは、Django の機能を利用します。
ユーザー登録signupと、パスワード変更password_changeは、Django の機能をカスタマイズします。
accounts/urls.py
app_name = 'accounts'
urlpatterns = [
path('login/', auth_views.LoginView.as_view(
success_url = '/',
template_name = 'accounts/login.html',
), name='login'),
path('logout/',auth_views.LogoutView.as_view(next_page = '/'), name='logout'),
path('signup/', views.Signup.as_view(), name='signup'),
path('password_change/', views.PasswordChange.as_view(), name='password_change'),
]
ユーザー登録フォーム
accounts/forms.py
{% endblock %}
class SignupForm(auth_forms.UserCreationForm):
class Meta:
model = auth_models.User
fields = ('username', 'password1', 'password2')
一般ユーザー機能ビュー ユーザー登録
CreateViewを継承し実装します。
ユーザー登録後、主アプリケーションページに遷移します。
accounts/views.py Signup
class Signup(CreateView):
form_class = forms.SignupForm
template_name = 'accounts/signup.html'
success_url = '/'
def form_valid(self, form):
user = form.save()
login(self.request, user)
self.object = user
return redirect(self.get_success_url())
一般ユーザー機能ビュー パスワード変更
PasswordChangeViewを継承し実装します。
パスワード変更後、主アプリケーションページに遷移し、メッセージを表示します。
accounts/views.py PasswordChange
class PasswordChange(auth_views.PasswordChangeView):
template_name = 'accounts/password_change.html'
success_url = '/'
def form_valid(self, form):
res = super().form_valid(form)
messages.success(self.request, 'Password Changed.')
return res
ログインテンプレート
templates/login.html
{% extends 'base.html' %}
{% block content %}
<div>
<h1>Login</h1>
<form method="post">
{% csrf_token %}
<table>
{{ form }}
</table>
<input type="submit" value="Submit">
</form>
<a href="{% url 'accounts:signup' %}">Signup</a>
</div>
{% endblock %}
パスワード変更テンプレート
templates/password_change.html
{% extends 'base.html' %}
{% block content %}
<div>
<h1>Password Change</h1>
<p>Login {{ user.username }}</p>
<form method="post">
{% csrf_token %}
<table>
{{ form }}
</table>
<input type="submit" value="Submit">
</form>
</div>
{% endblock %}
ユーザー登録テンプレート
templates/signup.html
{% extends 'base.html' %}
{% block content %}
<div>
<h1>Signup</h1>
<form method="post">
{% csrf_token %}
<table>
{{ form }}
</table>
<input type="submit" value="Submit">
</form>
</div>
{% endblock %}
主アプリケーション
sample/urls.py
app_name = 'sample'
urlpatterns = [
path('', views.index, name='index'),
]
sample/views.py
@login_required
def index(request):
return render(request, 'sample/index.html')
sample/index.py
{% extends 'base.html' %}
{% block content %}
<div>
<div>
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
<h1>Index</h1>
<p>Login {{ user.username }}</p>
<div>
<a href="{% url 'accounts:password_change' %}">Password Change</a>
<a href="{% url 'accounts:logout' %}">Logout</a>
</div>
</div>
{% endblock %}