44
36

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Djangoでaccountsアプリケーションを使わずにログイン認証する

Last updated at Posted at 2019-02-10

DjangoでWebサイトを作る際に、accountsアプリケーションを作成することなくログイン認証をする必要に迫られたのですが、包括的、体系的に説明しているサイトに出会えなかったため書き残します。

環境

  • Python 3.7.0
  • Django 2.1.4

(2020/3月追記: Python 3.7.4, Django 3.0でも動作確認済みです)

ディレクトリ構成

ディレクトリは以下のように構成されています。ここでは、ログイン認証に必要な最低限のものを書きます。

mysite/
 ├ manage.py
 ├ mysite/
 │  ├ settings.py
 │  └ urls.py
 └ myapp/
    ├ urls.py
    ├ views.py
    ├ forms.py
    ├ models.py
    └ templates/
      ├ base.html
      └ myapp/
        ├ mymodel_detail.html
        ├ signup.html
        └ などいろいろなhtml

よくあるログイン認証の紹介では、accountsアプリケーションを使うために、

$ python manage.py startapp accounts

を用いて、mysite/accounts/を作成、mysite/mysite/settings.pyのINSTALLED_APPSに'accounts.apps.AccountsConfig'を追加する、とあると思いますが、今回はINSTALLED_APPSにaccountsアプリケーションを追加せずにログイン認証をします

なお、今回の場合でもログイン認証だけを考えるならばmysite/mysite/models.pyの変更は不要です。

前提となる設定

まず、mysite/mysite/settings.pymyappをアプリケーションとして追加しています

mysite/mysite/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'myapp' # Here
]

また、ルートのURLとして、myappを登録しています。

mysite/mysite/settings.py
ROOT_URLCONF = 'myapp.urls'

さらに、ログインしなければログイン関係やサインアップ関係のページ以外は一切見れないようにします(設定方法は後述)。

必要な設定

最初に必要な設定をまとめると、次のようになります。

  • mysite/mysite/settings.pyにログインやログアウトのためのURLとそれぞれのリダイレクト先を明記
  • mysite/myapp/urls.pyにログイン、ログアウトなどのURLを登録
  • mysite/myapp/views.py必要ならばログイン、ログアウトなどを登録、さらにログイン時以外に見れないようにする
  • mysite/myapp/templates/にログインなどのhtmlを作成

mysite/mysite/settings.pyの設定

以下では、名前付きURLパターンを使います。これにより、アプリケーションごとに名前空間の分離を図ることができます。myappをアプリケーションとしてsettings.pyで登録していることに注意してください

名前付きURLパターンの公式のマニュアル
https://docs.djangoproject.com/ja/2.1/topics/http/urls/#naming-url-patterns

django.contrib.auth、つまりログインなどに関するURLの公式のマニュアル
https://docs.djangoproject.com/ja/2.1/ref/settings/#auth

ログイン、ログアウトのURLの設定

ログイン、ログアウトのURLに用いるURLは、通常はaccountsアプリケーションの存在を仮定しています。この設定のデフォルトでは、

LOGIN_URL='accounts/login/'     # ログイン
LOGOUT_URL='accounts/logout/'   # ログアウト

とされているため、今回は、

mysite/mysite/settings.py
LOGIN_URL='myapp:login'     # ログイン
LOGOUT_URL='myapp:logout'   # ログアウト

と設定します。myapp:loginなどとしているのは名前付きURLパターンを用いているためです。ここで、 loginlogoutmyapp.views のなかで、ログイン、ログアウトのURLを name='login' などと名前の登録をすることで使えるようになり、この機能と同じ myapp.views 内の app_name='myapp' の登録により、URLを変更したとしても名前で追うことができ、ハードコーディングをしないでいいようになります。(これらの登録も後の項目で載せます)

リダイレクト先の設定

ログイン、ログアウトが完了した後に遷移するURL(リダイレクト先)を登録する必要もあります。デフォルト値も併せて紹介すると、

LOGIN_REDIRECT_URL='/accounts/profile/' # ログイン
LOGOUT_REDIRECT_URL='None'              # ログアウト

です。例えば、ログインした後に遷移するURLをmyapp/home、ログアウトした後に遷移するURLをmyapp/loginにする(ログインページにする)ならば、以下のように設定します。

mysite/mysite/settings.py
LOGIN_REDIRECT_URL='myapp:home'      # ログイン
LOGOUT_REDIRECT_URL='myapp:login'    # ログアウト

このようにリダイレクト先を設定しておけば、ログインやログアウトが完了すると自動的に画面が遷移します。

mysite/myapp/templates/の存在を明記すべきかどうか

mysite/mysite/settings.py
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        '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',
            ],
        },
    },
]

'DIRS': [os.path.join(BASE_DIR, 'templates')], 

とすべきと書いてあるページもありますが、今回はなくても大丈夫です。(もしもだめだったら追加してください)

mysite/myapp/urls.pyにログイン、ログアウトなどを登録

作成したものは以下のものです。

mysite/myapp/urls.py
from django.urls import path, include

from . import views # mysite/myapp/views.pyをインポート

from django.contrib.auth import views as auth_views

app_name = 'myapp'
urlpatterns = [
    path('home/', views.home, name='home'),
    path('login/', auth_views.LoginView.as_view(template_name='myapp/login.html'), name='login'),
    path('logout/', auth_views.LogoutView.as_view(), name='logout'),
    path('signup/', views.signup, name='signup'),
    path('password_change/', auth_views.PasswordChangeView.as_view(), name='password_change'),
    path('password_change/done/', auth_views.PasswordChangeDoneView.as_view(), name='password_change_done'),
#     path('password_reset/', auth_views.PasswordResetView.as_view(template_name='myapp/password_reset.html'), name='password_reset'),
#     path('password_reset/done/', auth_views.PasswordResetDoneView.as_view(), name='password_reset_done'),
#     path('reset/<uidb64>/<token>/', auth_views.PasswordResetConfirmView.as_view(), name='password_reset_confirm'),
#     path('reset/done/', auth_views.PasswordResetCompleteView.as_view(), name='password_reset_complete'),
]

ここでは、パスワード変更などのページについてはすべてコメントアウトしていますが、これらを用いれば他の機能も拡張できます。また、アプリケーションとしてmyappを用いるため、app_name = 'myapp'と書いていることに注意してください。

また、サインアップに関するビューはデフォルトでないですが、それ以外はdjango.contrib.authを使うことができます
accountsアプリケーションを用いる場合には一括でaccounts/下に一括で利用することができるようになっていますが、accountsアプリケーションを利用せずとも、個別に上に載せたものは上記のように利用することができます。

ログインなどに関するビューもデフォルトのままでよいなら自分でmysite/myapp/views.pyに登録する必要もありません(個別にカスタマイズしたいならこれらのクラスを継承したものを作成する必要があります)。

ここで、ログインのみ引数を与えましたが、これはテンプレートの存在場所を指定するためです。ログインやログアウトのhtmlは、デフォルトでは以下の通りです。

  • ログイン画面: templates/registration/login.html
  • ログアウト画面: templates/registration/logged_out.html

これは、上記のコードのようにas_viewの引数にtemplate_name=(mysite/myapp/templates/以下の場所の指定)と指定することで変更することができます。

mysite/myapp/views.py

関数の作成

ログインやログアウトなど、django.contrib.authのままでよいならば書く必要はありませんが、メールアドレス認証など、独自のクラスが必要ならば作成する必要がありますが、いらないならば特になにもしなくてよいです。

しかし、サインアップに関するビューはないため、自分で作る必要があります
例えば、次のようにして作成できます。

mysite/myapp/views.py
from django.contrib.auth.forms import UserCreationForm # ユーザー作成のため追加

def signup(request):
    if request.method == 'POST':
        form = UserCreationForm(request.POST)
        if form.is_valid():
            user = form.save()
            login(request, user)
            return redirect('myapp/main')
    else:
        form = UserCreationForm()
    return render(request, 'myapp/signup.html', {'form': form})

ログインしていないときに見れないようにする

ログインしていないときにページを見れないようにするためには、mysite/myapp/views.pyの途中で、

mysite/myapp/views.py
from django.contrib.auth.decorators import login_required

# ログインしていないときにも見れるページに関する関数
# 例えば、def signup(request)など

@login_required

# ログインしているときのみ見れるページに関する関数
# 例えば、def main(request)など

を書くとよいです。クラスを用いているときには、

views.py
from django.contrib.auth.mixins import LoginRequiredMixin

# ログインしているときのみ見れるページのクラスベースのビュー
def myclass(LoginRequiredMixin, ...):
    ...

のように、最初にLoginRequiredMixinを継承します

mysite/myapp/templates/にログインなどのhtmlを作成

ログインのためのhtmlを作成します。
詳細は省略しますが、

mysite/myapp/templates/myapp/login.html
<tr>
    <td>{{ form.username.label_tag }}</td>
    <td>{{ form.username }}</td>
</tr>
<tr>
    <td>{{ form.password.label_tag }}</td>
    <td>{{ form.password }}</td>
</tr>

のように、django.contrib.authLoginViewを用いているならば、{{ form.username }}{{ form.password }}でユーザー名とパスワードを利用でき、

mysite/myapp/templates/myapp/login.html
<input type="submit" id="login_button" class="login_button" name="login_button" value="ログイン" onclick="location.href='./../home'" />
<input name="next" type="hidden" value="{{ next }}"/>

などとすれば、ログインボタンが作成できます。

これで、ログインができるようになりました。

なお、logout用のhtmlファイルはログアウト後のページを作らないならば(今回の場合はログイン画面に遷移させているので)不要です。

まとめ

みなさん快適なDjangoライフを!

44
36
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
44
36

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?