Django と認証
〜 認証機能のキホンのキ 〜
2021-12-21 作成 福島
TOP > toys > django-auth

前置き

便利なはずの python の Web フレームワークが、非常に理解しにくいものになっています。
ここでは、なるべくシンプルに最低限の機能だけを使用して Django の認証ページを制作します。

ユーザの登録・削除は shell_plus を使用してコンソールから操作します。
専用の Web ページはここでは作成しません。

テンプレートのネスト (include) や条件分岐マクロ、定番の Bootstrap 等を導入していないのは、わざとです。
最初から使うと Django を理解しにくくなってしまいます。
それらが今すぐ必要な人は、そもそも本稿を読む必要がないと思いますが···。

0. 設置要件
サーバ
項目内容備考
OSCentOS Linux release 8.4.2105CentOS 8 のサポートも 2021 年限り。方針は突然変更しないでほしかった。
IP アドレス:HTTP ポート192.168.11.7:8000-
使用言語Python 3.6.8-
Python パッケージマネージャpip 21.2.4-
フレームワークDjango 3.2.6-
データベースSQLite 3.26.0Django に付属のもの。
Django のインストールがまだなら、インストールしておいてください。

設置場所
項目内容URL備考
設置ディレクトリ/home/who/-ユーザ名 who のホームディレクトリに設置する。
本稿説明では ~/ がホームディレクトリの意味。
プロジェクト名hello--
アプリケーションアカウントaccounts(*1)http://192.168.11.7:8000/accounts/login/ログイン・ログアウトページは
LoginView, LogoutView で実装する。
http://192.168.11.7:8000/accounts/logout/
コンテンツworld(*1)http://192.168.11.7:8000/通常コンテンツを表示する。
http://192.168.11.7:8000/world/数値(*2)/認証コンテンツを表示する。
http://192.168.11.7:8000/world/数値(*2)/edit/認証コンテンツを編集する。(今回はダミー)
(*1)アプリケーション名の先頭は小文字にする必要がある。
(*2)「数値」はサンプルのためのダミー。本稿では数値が変化しても同じページを表示する。

認証用メンバ
項目ユーザ IDメールアドレスパスワード備考
管理ユーザadminadmin@example.jpadminpassword-
通常ユーザuser1user1@example.jppassword-

本稿は大きく分けて以下の構成になっている。上から順に実施すること。
  項 1~3···プロジェクトの作成
  項 4~6···通常コンテンツの設置
  項 7~11···認証機能の追加

1. ソフトウェアの確認
1-1. OS のバージョンを確認する。
~/$ cat /etc/system-release
CentOS Linux release 8.4.2105
1-2. Python と pip のバージョンを確認する。
~/$ python3 --version
Python 3.6.8
~/$ pip --version
pip 21.2.4 from /usr/local/lib/python3.6/site-packages/pip (python 3.6)
1-3. Django のバージョンを確認する。
~/$ python3 -m django --version
3.2.6

2. Django プロジェクトの作成
2-1. django-admin コマンドの確認をする。
~/$ which django-admin
/usr/local/bin/django-admin
2-2. プロジェクトを作成する。
~/$ django-admin startproject hello
~/$ mkdir ~/hello/templates/
2-3. 作成したプロジェクトのディレクトリツリーを確認する。
~/$ LANG=c tree ~/hello/
/home/who/hello/
|-- hello
|   |-- __init__.py
|   |-- asgi.py
|   |-- settings.py
|   |-- urls.py
|   `-- wsgi.py
|-- manage.py
`-- templates
2-4. 作成したプロジェクトの初期設定をする。
~/$ cd ~/hello/
~/hello/$ vim ~/hello/hello/settings.py
 (省略)
ALLOWED_HOSTS = ['*']   # 全インタフェースからのリクエストを受け付ける。
 (省略)
INSTALLED_APPS = [
     (省略)
    'django_extensions',    # manage.py の shell_plus を使用可能にする。
]
 (省略)
TIME_ZONE = 'Asia/Tokyo'    # タイムゾーンを日本にする。(内部形式は変化しない。(UTC のまま)
 (省略)
2-5. shell_plus の動作確認をする。
~/hello/$ python3 ./manage.py shell_plus -c "for item in 'SHELL_PLUS_PRE_IMPORTS','SHELL_PLUS_IMPORTS','SHELL_PLUS_POST_IMPORTS': print(item + ' ' + str(getattr(settings, item, {})))"
shell_plus でインポートされるモジュールを確認。
# Shell Plus Model Imports
from django.contrib.admin.models import LogEntry
from django.contrib.auth.models import Group, Permission, User
from django.contrib.contenttypes.models import ContentType
from django.contrib.sessions.models import Session
# Shell Plus Django Imports
from django.core.cache import cache
from django.conf import settings
from django.contrib.auth import get_user_model
from django.db import transaction
from django.db.models import Avg, Case, Count, F, Max, Min, Prefetch, Q, Sum, When
from django.utils import timezone
from django.urls import reverse
from django.db.models import Exists, OuterRef, Subquery
SHELL_PLUS_PRE_IMPORTS {}
SHELL_PLUS_IMPORTS {}
SHELL_PLUS_POST_IMPORTS {}
2-6. プロジェクトの動作確認をする。
2-6-1. Django 付属のテストサーバ機能を起動する。
~/hello/$ python3 ./manage.py runserver 0:8000
「0:8000」は、当該サーバの全 IP アドレスのポート 8000 番で待ち受ける指定。

停止する場合は Ctrl - C を入力する。
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).

You have 18 unapplied migration(s). Your project may not work properly until you
apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
December 18, 2021 - 19:22:29
Django version 3.2.6, using settings 'hello.settings'
Starting development server at http://0:8000/
Quit the server with CONTROL-C.
適用されていない移行がある」と警告されている。
これは、後述「3. データベースの初期化」を実施すると警告されなくなる。
それまで admin, auth, contenttypes, sessions は使用できない。
2-6-2. ブラウザを起動し、テストサーバにアクセスする。
Windows10 の PowerShell から Firefox を起動する例
 >_ Windows PowerShell 
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

新しいクロスプラットフォームの PowerShell をお試しください https://aka.ms/pscore6

PS C:\Users\who> &"C:\Program Files\Mozilla Firefox\firefox.exe" 192.168.11.7:8000 
PS C:\Users\who>

Firefox の表示

2-6-3. テストサーバ実行後のプロジェクトのディレクトリツリーを確認する。
~/hello/$ LANG=c tree ~/hello/
/home/who/hello/
|-- db.sqlite3      (runserver の実行時に生成された。まだ空っぽ)(*3)
|-- hello
|   |-- __init__.py
|   |-- __pycache__                 (runserver の実行時に生成された)(*4)
|   |   |-- __init__.cpython-36.pyc (    〃    )
|   |   |-- settings.cpython-36.pyc (    〃    )
|   |   |-- urls.cpython-36.pyc     (    〃    )
|   |   `-- wsgi.cpython-36.pyc     (    〃    )
|   |-- asgi.py
|   |-- settings.py
|   |-- urls.py
|   `-- wsgi.py
|-- manage.py
`-- templates
(*3)db.sqlite3 は Django によって作られる。
(*4)__pycache__ (と *.pyc) が作られるのは Python が持っている機能による。

3. データベースの初期化
今回使用する Django 付属のログイン機能はデータベースが必要。
データベースが無い状態でログイン機能を使用すると、エラー表示され動作しない。
エラー表示の例:
django.db.utils.OperationalError: no such table: django_session
3-1. データベースのテーブル (スキーマ) を確認する。
~/hello/$ sqlite3 ./db.sqlite3 .table
(何も表示されない)
3-2. データベースのテーブルを作成する。
~/hello/$ python3 ./manage.py makemigrations
No changes detected
最初なので、以前の変更は何もない。
~/hello/$ python3 ./manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying sessions.0001_initial... OK
3-3. データベースのテーブルを確認する。
~/hello/$ sqlite3 ./db.sqlite3 .table
auth_group                  auth_user_user_permissions
auth_group_permissions      django_admin_log
auth_permission             django_content_type
auth_user                   django_migrations
auth_user_groups            django_session

4. コンテンツ用アプリケーションの用意
4-1. Django アプリケーション world を追加する。
4-1-1. アプリケーションを追加する。
~/hello/$ python3 ./manage.py startapp world
~/hello/$ LANG=c tree ~/hello/
/home/who/hello/
|-- db.sqlite3
|-- hello
 (省略)
|-- manage.py
|-- templates
`-- world               (startapp world の実行時に生成された)
    |-- __init__.py     (    〃    )
    |-- admin.py        (    〃    )
    |-- apps.py         (    〃    )
    |-- migrations      (    〃    )
    |   `-- __init__.py (    〃    )
    |-- models.py       (    〃    )
    |-- tests.py        (    〃    )
    `-- views.py        (    〃    )
4-1-2. アプリケーションをプロジェクトに追加する。
~/hello/$ cat ~/hello/world/apps.py
from django.apps import AppConfig


class WorldConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'world'
~/hello/$ vim ~/hello/hello/settings.py
 (省略)
INSTALLED_APPS = [
     (省略)
    'django_extensions',
    'world.apps.WorldConfig',       # ← 追加
]
 (省略)
4-2. 通常コンテンツの作成
4-2-1. テンプレートファイルを作成する。
~/hello/$ mkdir -p ~/hello/world/templates/world/
~/hello/$ echo 'Hello world' > ~/hello/world/templates/world/top.html
~/hello/$ cat ~/hello/world/templates/world/top.html
Hello world
4-2-2. ディスパッチャを設定する。
~/hello/$ vim ~/hello/world/views.py
from django.shortcuts import render

# Create your views here.

def top(request):                               # ← 追加する
    return render(request, 'world/top.html')    # ← 追加する
~/hello/$ vim ~/hello/hello/urls.py
"""hello URL Configuration
   (省略)
"""
from django.contrib import admin
from django.urls import path

from world.views import top     # ← 追加する 
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', top, name='top'),  # ← 追加する
]

5. 通常コンテンツのユニットテスト
5-1. ユニットテストの定義を作成する。
~/hello/$ vim ~/hello/world/tests.py
from django.test import TestCase

# Create your tests here.

class TopPageTest(TestCase):

    # ステータスコードのチェック
    def test_response(self):
        res = self.client.get("/")
        self.assertEqual(res.status_code, 200)

    # コンテンツのチェック
    def test_content(self):
        res = self.client.get("/")
        self.assertEqual(res.content, b'Hello world\n')
各チェック用関数の名称は先頭に test_ を付加して記述する。
5-2. ユニットテストを実行する。
~/hello/$ python3 ./manage.py test
· 問題がない場合
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
..
----------------------------------------------------------------------
Ran 2 tests in 0.011s

OK                      # ← OK が表示されること
Destroying test database for alias 'default'...
ここでは見易いように太字にしています。実際は太字になりません。
· 問題がある場合
ユニットテストの結果が FAILED になる場合は、ここまで作成したファイルのどこかが指定と異なっている。
ユニットテスト自体に誤りがあるかもしれない。
誤りを修正して結果を OK にすること。

下記では作成したテスト test_content(), test_response() でエラーが発行されている。
テストは定義順でなく、アルファベット順に実行される。
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
FF
======================================================================
FAIL: test_content (world.tests.TopPageTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/who/hello/world/tests.py", line 13, in test_content
    self.assertEqual(res.content, b'Hello world\n')
AssertionError: b'hello world\n' != b'Hello world\n'

======================================================================
FAIL: test_response (world.tests.TopPageTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/who/hello/world/tests.py", line 9, in test_response
    self.assertEqual(res.status_code, 200)
AssertionError: 404 != 200

----------------------------------------------------------------------
Ran 2 tests in 0.198s

FAILED (failures=2)     # ← FAILED が表示された
Destroying test database for alias 'default'...
ここでは見易いように太字にしています。実際は太字になりません。

6. 認証コンテンツページの作成
6-1. テンプレートファイルを作成する。
~/hello/$ vim ~/hello/world/templates/world/display.html (新規作成)
<html>
<head>
<meta charset=utf-8>
<title>World Display</title>
</head>
<body>
<p>World Display</p>
<p>ID は {{ ID }} です。</p>
</body>
</html>
~/hello/$ vim ~/hello/world/templates/world/edit.html (新規作成)
<html>
<head>
<meta charset=utf-8>
<title>World Edit</title>
</head>
<body>
<p>World Edit</p>
<p>ID は {{ ID }} です。</p>
</body>
</html>
6-2. ディスパッチャを設定する。
~/hello/$ vim ~/hello/world/views.py
from django.shortcuts import render

# Create your views here.

def top(request):
    return render(request, 'world/top.html')

def world_display(request, world_id):                               # ← 追加する
    return render(request, 'world/display.html', {'ID':world_id})   # ← 追加する

def world_edit(request, world_id):                                  # ← 追加する
    return render(request, 'world/edit.html',    {'ID':world_id})   # ← 追加する
~/hello/$ vim ~/hello/world/urls.py (新規作成)
from django.urls import path
from world import views
urlpatterns = [
    path('<int:world_id>/',      views.world_display, name='world_displlay'),
    path('<int:world_id>/edit/', views.world_edit,    name='world_edit'    ),
]
~/hello/$ vim ~/hello/hello/urls.py
"""hello URL Configuration
  (省略)
"""
from django.contrib import admin
from django.urls import path
from django.urls import include             # ← 追加する

from world.views import top
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', top, name='top'),
    path('world/', include('world.urls')),  # ← 追加する
]
6-3. 動作確認
6-3-1. テストサーバを起動する。
~/hello/$ python3 ./manage.py runserver 0:8000
6-3-2. ブラウザを起動し、テストサーバにアクセスする。
認証機能を組み込んでいないため、まだ「認証コンテンツ」になっていない。
 >_ Windows PowerShell 
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

新しいクロスプラットフォームの PowerShell をお試しください https://aka.ms/pscore6

PS C:\Users\who> &"C:\Program Files\Mozilla Firefox\firefox.exe" 192.168.11.7:8000/         ← 通常コンテンツを表示する場合
PS C:\Users\who> &"C:\Program Files\Mozilla Firefox\firefox.exe" 192.168.11.7:8000/world/1         ← 認証コンテンツを表示する場合
PS C:\Users\who> &"C:\Program Files\Mozilla Firefox\firefox.exe" 192.168.11.7:8000/world/1/edit    ← 認証コンテンツを編集する場合
PS C:\Users\who>

通常コンテンツを表示


認証コンテンツを表示


編集コンテンツを表示 (ここでは編集機能を実装しません)


7. ログイン用アプリケーションの用意
View に認証機能を組み込む前に、認証機能の一部となるログインページを作成する。

7-1. Django アプリケーション accounts を追加する。
7-1-1. アプリケーションを追加する。
~/hello/$ python3 ./manage.py startapp accounts
~/hello/$ LANG=c tree ~/hello/
/home/who/hello/
|-- accounts            (startapp accounts の実行時に生成された)
|   |-- __init__.py     (    〃    )
|   |-- admin.py        (    〃    )
|   |-- apps.py         (    〃    )
|   |-- migrations      (    〃    )
|   |   `-- __init__.py (    〃    )
|   |-- models.py       (    〃    )
|   |-- tests.py        (    〃    )
|   `-- views.py        (    〃    )
|-- db.sqlite3
|-- hello
 (省略)
|-- manage.py
|-- templates
`-- world
 (省略)
7-1-2. アプリケーションをプロジェクトに追加する。
~/hello/$ cat ~/hello/accounts/apps.py
from django.apps import AppConfig


class AccountsConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'accounts'
~/hello/$ vim ~/hello/hello/settings.py
 (省略)
INSTALLED_APPS = [
     (省略)
    'django_extensions',
    'world.apps.WorldConfig',
    'accounts.apps.AccountsConfig', # ← 追加
]
 (省略)

8. ログイン・ログアウトページの作成
8-1. 認証用 URL (ログイン要求時にリダイレクトされる URL) を確認する。
~/hello/$ python3 ./manage.py shell_plus --quiet-load -c "from django.contrib.auth.urls import urlpatterns
for url in urlpatterns: print(url)"
<URLPattern 'login/' [name='login']>
<URLPattern 'logout/' [name='logout']>
<URLPattern 'password_change/' [name='password_change']>
<URLPattern 'password_change/done/' [name='password_change_done']>
<URLPattern 'password_reset/' [name='password_reset']>
<URLPattern 'password_reset/done/' [name='password_reset_done']>
<URLPattern 'reset///' [name='password_reset_confirm']>
<URLPattern 'reset/done/' [name='password_reset_complete']>
~/hello/$ python3 ./manage.py shell_plus --quiet-load -c "print(settings.LOGIN_URL)"
/accounts/login/
8-2. テンプレートファイルを作成する。
~/hello/$ mkdir -p ~/hello/accounts/templates/registration/
ディレクトリ名を registration/ としているのは前バージョンからの名残り。整合性が合っていれば良いので、深い意味はない。

~/hello/$ vim ~/hello/accounts/templates/registration/login.html (新規作成)
<html>
<head>
<meta charset=utf-8>
<title>ログインページ</title>
</head>
<body>
<p>
<form method=post>
{% csrf_token %}
<input type=hidden name=next value="{{ next }}" />
ログイン<br>
ユーザ名 <input type=text name=username maxlength=150><br>
パスワード <input type=password name=password><br>
<button type=submit>ログインする</button><br>
</form>
</p>
</body>
</html>
~/hello/$ vim ~/hello/accounts/templates/registration/logout.html (新規作成)
<html>
<head>
<meta charset=utf-8>
<title>ログアウトページ</title>
</head>
<body>
<p>
ログアウトしました<br>
<a href="/">トップページへ</a><br>
</p>
</body>
</html>
8-3. ログイン・ログアウト用の View を urls.py に用意する。
ログイン・ログアウトページは contrib.auth.views の LoginView / LogoutView を使用するので views.py を使用しない。

~/hello/$ vim ~/hello/accounts/urls.py (新規作成)
from django.urls import path
from django.contrib.auth.views import LoginView, LogoutView

urlpatterns = [

    path(route='login/', name='login',
        view=LoginView.as_view(
            redirect_authenticated_user=True,
            template_name='registration/login.html',
        ),
    ),

    path(route='logout/', name='logout',
        view=LogoutView.as_view(
            template_name='registration/logout.html',
        ),
    ),
]
route='···' は上記 8-1 で確認した URL。
8-4. ディスパッチャを設定する。
~/hello/$ vim ~/hello/hello/urls.py
"""hello URL Configuration
  (省略)
"""
from django.contrib import admin
from django.urls import path
from django.urls import include

from world.views import top
urlpatterns = [
      (省略)
    path('', top, name='top'),
    path('world/', include('world.urls')),
    path('accounts/', include('accounts.urls')),    # ← 追加する
]

9. 認証機能の組み込み
9-1. 認証用デコレータを確認する。
~/hello/$ python3 ./manage.py shell_plus -c "from django.contrib.auth import decorators
help(decorators)" | cat
デコレータ login_required / permission_required / user_passes_test の存在が確認できる。
今回は login_required を使用する。
# Shell Plus Model Imports
    (省略)

NAME
    django.contrib.auth.decorators

FUNCTIONS
    login_required(function=None, redirect_field_name='next', login_url=None)
        Decorator for views that checks that the user is logged in, redirecting
        to the log-in page if necessary.

    permission_required(perm, login_url=None, raise_exception=False)
        Decorator for views that checks whether a user has a particular permission
        enabled, redirecting to the log-in page if necessary.
        If the raise_exception parameter is given the PermissionDenied exception
        is raised.

    user_passes_test(test_func, login_url=None, redirect_field_name='next')
        Decorator for views that checks that the user passes the given test,
        redirecting to the log-in page if necessary. The test should be a callable
        that takes the user object and returns True if the user passes.

DATA
    REDIRECT_FIELD_NAME = 'next'
    settings = <LazySettings "hello.settings">

FILE
    /usr/local/lib/python3.6/site-packages/django/contrib/auth/decorators.py
9-2. View に認証機能 (ログイン要求) を組み込む。
~/hello/$ vim ~/hello/world/views.py
from django.shortcuts import render
from django.contrib.auth.decorators import login_required    # ← 追加する

# Create your views here.

# top は通常コンテンツなので認証の対象外。(@login_required を記述しない)
def top(request):
    return render(request, 'world/top.html')

@login_required                     # ← 追加する
def world_display(request, world_id):
    return render(request, 'world/display.html', {'ID':world_id})

@login_required                     # ← 追加する
def world_edit(request, world_id):
    return render(request, 'world/edit.html',    {'ID':world_id})
9-3. テンプレートにログアウトの機能を追加する。
この認証機能はクッキーによって許可情報を保持するため、 認証ページに無事ログインしたあとは、
クッキーが消滅するまで、ブラウザを終了してもログインしたままになります。(2 週間)
クッキーを無効にするため、ログアウトへのリンクを認証コンテンツのテンプレートに追加します。

~/hello/$ vim ~/hello/world/templates/world/display.html
<html>
<head>
<meta charset=utf-8>
<title>World Display</title>
</head>
<body>
<p>World Display</p>
<p>ID は {{ ID }} です。</p>
<p><a href=/accounts/logout/>ログアウトする</a></p>      # ← 追加する
</body>
</html>
~/hello/$ vim ~/hello/world/templates/world/edit.html
<html>
<head>
<meta charset=utf-8>
<title>World Edit</title>
</head>
<body>
<p>World Edit</p>
<p>ID は {{ ID }} です。</p>
<p><a href=/accounts/logout/>ログアウトする</a></p>      # ← 追加する
</body>
</html>
クッキーの生存時間を確認するにはこちら。(日単位)
~/hello/$ python3 ./manage.py shell_plus --quiet-load -c "print(settings.SESSION_COOKIE_AGE/(24*3600))"
14.0

10. 認証ユーザの作成
10-1. 管理ユーザを作成する。
~/hello/$ python3 ./manage.py createsuperuser --username=admin --email=admin@example.jp
Password: adminpassword 
Password (again): adminpassword 
Superuser created successfully.
10-2. 通常ユーザを作成する。
~/hello/$ python3 ./manage.py shell_plus -c "
User.objects.create_user(username='user1', email='user1@example.jp', password='password')"
10-3. 作成したユーザを確認する。
~/hello/$ python3 ./manage.py shell_plus --quiet-load -c "
for user in User.objects.all():
 print(user.id, user.username, user.email)
 print(user.password)"
↑ 行頭の空白文字も忘れずに。
1 admin admin@example.jp
pbkdf2_sha256$260000$EdRL9JGDbcYtyw2vsCc1xC$gPZqn1JOmKzTAtA1MQHC8S+U6iOTZmPe1xVH/4sVzyU=
2 user1 user1@example.jp
pbkdf2_sha256$260000$AxaXla31DZwBGZbeBMkOF7$KDSgqIiPh8yODDEr+wWdn/2Eze9LNKlwGRM0Q7WqIeQ=
10-A. ユーザを削除する場合はこちら。
~/hello/$ python3 ./manage.py shell_plus -c "User.objects.get(username='user1', is_superuser=False).delete()"
管理ユーザを削除するときは is_superuser=True を指定する。

11. 動作確認
11-1. テストサーバを起動する。
~/hello/$ python3 ./manage.py runserver 0:8000
11-2. ブラウザを起動し、テストサーバにアクセスする。
認証機能を組み込んだため、認証コンテンツをアクセスしようとするとログインページにリダイレクトされる。
 >_ Windows PowerShell 
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

新しいクロスプラットフォームの PowerShell をお試しください https://aka.ms/pscore6

PS C:\Users\who> &"C:\Program Files\Mozilla Firefox\firefox.exe" 192.168.11.7:8000/         ← 通常コンテンツを表示する場合
PS C:\Users\who> &"C:\Program Files\Mozilla Firefox\firefox.exe" 192.168.11.7:8000/world/1         ← 認証コンテンツを表示する場合
PS C:\Users\who> &"C:\Program Files\Mozilla Firefox\firefox.exe" 192.168.11.7:8000/world/1/edit    ← 認証コンテンツを編集する場合
PS C:\Users\who>

認証コンテンツを表示しようとすると、ログインページが表示される。

上記 10 で作成したユーザの ID, パスワードを記入し
ボタンをクリックする。
(ログインが失敗すると再度ログインページが表示される)

ログインが成功すれば、目的のページが表示される。

「ログアウトする」をクリックすると、
ブラウザに保持されている認証用クッキーが無効になり、ログアウトページが表示される。