LoginSignup
0
1

More than 1 year has passed since last update.

DjangoとDocker練習O8o1o0 ログイン(ユーザー認証)のページを作ろう!

Last updated at Posted at 2022-05-17

サンプルを見る

📖 この記事のゴール:ログイン
📖 この記事のゴール:ログアウト

目標

見た目がマシな ログイン(ユーザー認証)のページがほしい

情報

この記事は Lesson 1. から順に全部やってこないと ソースが足りず実行できないので注意されたい

What is This is
Lesson 1. 📖 DjangoとDockerでゲーム対局サーバーを作ろう!

この記事のアーキテクチャ:

What is This is
OS Windows10
Container Docker
Editor Visual Studio Code (以下 VSCode と表記)

ディレクトリ構成を抜粋すると 以下のようになっている

    ├── 📂 src1
    │   ├── 📂 apps1
    │   │   ├── 📂 portal_v1                # アプリケーション
    │   │   └── 📂 practice_vol1o0              # アプリケーション
    │   ├── 📂 data
    │   ├── 📂 project1                     # プロジェクト
    │   │   ├── 📄 __init__.py
    │   │   ├── 📄 asgi.py
    │   │   ├── 📄 settings.py
    │   │   ├── 📄 urls_practice.py
    │   │   ├── 📄 urls.py
    │   │   └── 📄 wsgi.py
    │   ├── 📂 project2
    │   ├── 🐳 docker-compose-project2.yml
    │   ├── 🐳 docker-compose.yml
    │   ├── 🐳 Dockerfile
    │   ├── 📄 manage.py
    │   └── 📄 requirements.txt
    ├── 📂 src1_meta
    │   ├── 📂 data
    │   │   └── 📄 urls.csv
    │   └── 📂 scripts
    │       └── 📂 auto_generators
    │           └── 📄 urls.py
    └── 📄 .gitignore

手順

Step O8o1o0g1o0 Dockerコンテナの起動

👇 (していなければ) Docker コンテナを起動しておいてほしい

# docker-compose.yml ファイルを置いてあるディレクトリーへ移動してほしい
cd src1

# Docker コンテナ起動
docker-compose up

Step O8o1o0g2o0 テンプレート作成 - login.html ファイル

👇 以下のファイルを新規作成してほしい

    └── 📂 src1                            # あなたの開発用ディレクトリー。任意の名前
        └── 📂 apps1
            └── 📂 accounts_vol1o0    # アプリケーション
                └── 📂 templates
                    └── 📂 account          # allauth のディレクトリー構成を真似ます
👉                      └── 📄 login.html
<!-- BOF O8o1o0g2o0 -->
<!--
    📖[login.html](https://github.com/pennersr/django-allauth/blob/master/allauth/templates/account/login.html)
-->

<!--
    # See also: 📖[Custom Signup View in django-allauth](https://tech.serhatteker.com/post/2020-06/custom-signup-view-in-django-allauth/)
-->
{% load static %} {# 👈あとで static "URL" を使うので load static します #}
<!-- -->
{% load i18n %}
<!-- -->
{% load account socialaccount %}
<!-- -->
{% get_providers as socialaccount_providers %}
<!-- -->
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet" />
        <link href="https://cdn.jsdelivr.net/npm/@mdi/font@6.x/css/materialdesignicons.min.css" rel="stylesheet" />
        <link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet" />
        <link rel="shortcut icon" type="image/png" href="{% static 'favicon.ico' %}" />
        <!--                                                ===================
                                                            1
            1. Example: `http://example.com/static/favicon.ico`
                                            ==================
        -->
        <title>サインイン</title>
    </head>
    <body>
        <div id="app">
            <v-app>
                <!-- v-app-bar に app プロパティを指定しないなら、背景画像を付けてほしい -->
                <v-app-bar app dense elevation="4">
                    <v-app-bar-nav-icon></v-app-bar-nav-icon>
                    <v-toolbar-title>サインイン</v-toolbar-title>
                </v-app-bar>
                <v-main>
                    <v-container>
                        <h3>もし会員登録をしてないなら</h3>
                        {% if socialaccount_providers %}

                        <!-- 👇ここらへん分からない -->
                        <p>{% blocktrans with site.name as site_name %}Please sign in with one of your existing third party accounts. Or, <a href="{{ signup_url }}">sign up</a> for a {{ site_name }} account and sign in below:{% endblocktrans %}</p>
                        <div class="socialaccount_ballot">
                            <ul class="socialaccount_providers">
                                <!-- -->
                                {% include "socialaccount/snippets/provider_list.html" with process="login" %}
                                <!-- -->
                            </ul>

                            <div class="login-or">or</div>
                        </div>
                        <!-- -->
                        {% include "socialaccount/snippets/login_extra.html" %}
                        <!-- 👆ここらへん分からない -->

                        <!-- -->
                        {% else %}
                        <!-- 👇こっちが出てくる -->
                        <p>もし あなたがアカウントを まだ作っていないなら、まず <v-btn class="my-4" color="primary" :href="createPathOfSignup()">サインアップ</v-btn> してください</p>
                        <!-- 👆こっちが出てくる -->
                        {% endif %}
                        <!-- -->
                    </v-container>
                    <v-container>
                        <h3>サインイン(利用開始)</h3>
                        <form class="login" method="POST" :action="createPathOfSignin()">
                            <!-- -->
                            {% csrf_token %}
                            <!-- 手動フォーム作成 ここから -->
                            {{ form.non_field_errors }}

                            <!-- ユーザー名 -->
                            <div class="fieldWrapper">
                                {{ form.login.errors }}
                                <v-text-field name="login" v-model="vu_userName.value" :rules="vu_userName.rules" counter="16" label="ユーザー名" required hint="使える文字 a-z, 0-9. 先頭に数字は使えません。 最大 16 文字"></v-text-field>
                            </div>

                            <div class="fieldWrapper">
                                {{ form.password.errors }}
                                <v-text-field type="password" name="password" v-model="vu_password" counter label="パスワード" required></v-text-field>
                            </div>
                            <div class="fieldWrapper">
                                {{ form.remember.errors }}
                                <v-checkbox v-model="vu_rememberFlag" label="パスワードを入力したままにする"></v-checkbox>
                            </div>
                            <!-- 手動フォーム作成 ここまで -->
                            <!-- -->
                            {% if redirect_field_value %}
                            <!-- -->
                            <input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
                            <!-- -->
                            {% endif %}
                            <!-- -->
                            <v-btn class="my-4" color="primary" type="submit">サインイン</v-btn>
                            <a class="button secondaryAction" href="{% url 'account_reset_password' %}">パスワードを忘れたら</a>
                        </form>
                    </v-container>
                </v-main>
            </v-app>
        </div>

        <script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>

        <script src="{% static 'accounts_vol1o0/form_html_parser/ver1o0.js' %}"></script>
        <!--            ==================================================
                        1
            1. `src1/apps1/accounts_vol1o0/static/accounts_vol1o0/form_html_parser/ver1o0.js`
                                           =================================================
        -->

        <script>
            let vue1 = new Vue({
                el: "#app",
                vuetify: new Vuetify(),
                data: {
                    // "vu_" は 「vue1.dataのメンバー」 の目印

                    // HTMLタグ文字列が渡されるので、解析します
                    vu_loginFormDoc: new DjangoAllauthFormParser().parseHtmlString("login", "{{ form.login|escapejs }}"),

                    // ユーザー名
                    vu_userName: {
                        value: "",
                        rules: [
                            // FIXME ここでルールを色々書いているが、モデル側で対応していないので、モデル側も対応してほしい
                            (value) => !!value || "Required", // 空欄の禁止
                            (v) => v.length <= 16 || "Max 16 characters", // 文字数上限
                            (value) => {
                                const pattern = /^[a-z][a-z0-9]*$/; // 正規表現で指定
                                return pattern.test(value) || "Invalid format";
                            },
                        ],
                    },

                    vu_passwordFormDoc: new DjangoAllauthFormParser().parseHtmlString("password", "{{ form.password|escapejs }}"),
                    vu_password: "",

                    vu_rememberFormDoc: new DjangoAllauthFormParser().parseHtmlString("form", "{{ form.remember|escapejs }}"),
                    vu_rememberFlag: false,

                    // vu_pathOfSignup: "{{ signup_url }}", // django-allauth のデフォルト
                    vu_pathOfSignup: "{% url 'signup' %}",
                    // vu_pathOfSignin: "{% url 'account_login' %}", // django-allauth のデフォルト
                    vu_pathOfSignin: "{% url 'login' %}",
                    {% block vue1_data_footer %}
                    {% endblock vue1_data_footer %}
                },
                methods: {
                    createPathOfSignin() {
                        let path = `${location.protocol}//${location.host}${this.vu_pathOfSignin}`;
                        //          --------------------  ---------------]-----------------------
                        //          1                     2               3
                        // 1. protocol
                        // 2. host
                        // 3. path
                        console.log(`SignIn path=[${path}]`);
                        return path;
                    },
                    createPathOfSignup() {
                        let path = `${location.protocol}//${location.host}${this.vu_pathOfSignup}`;
                        //          --------------------  ---------------]-----------------------
                        //          1                     2               3
                        // 1. protocol
                        // 2. host
                        // 3. path
                        console.log(`SignUp path=[${path}]`);
                        return path;
                    },
                },
            });
        </script>
    </body>
</html>
<!-- EOF O8o1o0g2o0 -->

Step O8o1o0g3o0 ビュー作成 - login フォルダー

👇 以下のファイルを新規作成してほしい

    └── 📂 src1
        └── 📂 apps1
            └── 📂 accounts_vol1o0    # アプリケーション
                ├── 📂 templates
                │   └── 📂 account
                │       └── 📄 login.html
                └── 📂 views
                    └── 📂 login
                        └── 📂 ver1o0
👉                          └── 📄 __init__.py
# BOF O8o1o0g3o0

# See also: 📖[Custom Signup View in django-allauth](https://tech.serhatteker.com/post/2020-06/custom-signup-view-in-django-allauth/)
from allauth.account.views import LoginView


class AccountsV1LoginView(LoginView):
    """django-allauth のログイン ビューをカスタマイズします
    📖[views.py](https://github.com/pennersr/django-allauth/blob/master/allauth/account/views.py)
    """

    # ファイルパス(使ってるか分からない)
    template_name = "accounts_vol1o0/templates/account/login.html"
    #                                          ------------------
    #                                          11
    #                --------------------------------------------
    #                1
    # 11. Allauthのディレクトリー構成に合わせる
    # 1. `src1/apps1/accounts_vol1o0/templates/accounts/login.html` を取得
    #                ---------------------------------------------

# EOF O8o1o0g3o0

Step O8o1o0g4o0 サブ ルート作成 - urls_accounts_vol1o0.py

URLの自動生成をしたくても、作りが他と異なるので、手作業で設定してほしい

    └── 📂 src1
        ├── 📂 apps1
        │   └── 📂 accounts_vol1o0    # アプリケーション
        │       ├── 📂 templates
        │       │   └── 📂 account
        │       │       └── 📄 login.html
        │       └── 📂 views
        │           └── 📂 login
        │               └── 📂 ver1o0
        │                   └── 📄 __init__.py
        └── 📂 project1
👉          ├── 📄 urls_accounts_vol1o0.py          # こちら
❌          └── 📄 urls.py                   # これではない
# ...略...


urlpatterns = [
    # ...中略...


    # O8o1o0g4o0 ログイン(ユーザー認証)
    path("accounts/vol1.0/login/", view=AccountsV1LoginView.as_view(),
         # ---------------------        -----------------------------
         # 1                            2
         name="login"),
    #          -----
    #          3
    # 1. 例えば `http://example.com/accounts/vol1.0/login/` のような URL のパスの部分
    #                              -----------------------
    # 2. allauth の LoginView をカスタマイズしたオブジェクト
    # 3. HTMLテンプレートの中で {% url 'login' %} のような形でURLを取得するのに使える
]

Step O8o1o0g5o0 Web画面へアクセス

📖 http://localhost:8000/accounts/vol1.0/login/

👆 ログイン ページを開く

既にログインしているなら、

📖 http://localhost:8000/accounts/vol1.0/logout/

👆 ログアウトを試してほしい

Step O8o1o0g6o0 ランチャーのリンク用データ追加 - finished-lessons.csv ファイル

👇 以下の既存ファイルの最終行に追記してほしい

    └── 📂 src1
        ├── 📂 apps1
        │   ├── 📂 portal_v1
        │   │   └── 📂 data
👉      │   │       └── 📄 finished-lessons.csv
        │   └── 📂 accounts_vol1o0    # アプリケーション
        │       ├── 📂 templates
        │       │   └── 📂 account
        │       │       └── 📄 login.html
        │       └── 📂 views
        │           └── 📂 login
        │               └── 📂 ver1o0
        │                   └── 📄 __init__.py
        └── 📂 project1
            ├── 📄 urls_accounts_vol1o0.py
            └── 📄 urls.py

👇 冗長なスペース,冗長なダブルクォーテーション,末尾のカンマ は止めてほしい

/accounts/vol1.0/login/,O8o1o0g6o0 アカウント1.0巻 ログイン(ユーザー認証)

👇 ランチャーにリンクが追加されていることを確認してほしい

📖 http://localhost:8000/

次の記事

📖 Djangoでログインしていないと見れないページ,およびログアウト機能を付けよう!

関連する記事

📖 login.html - テンプレートの原型

form関連

📖 Working with forms - 一番詳しい
📖 forms.py - 原型
📖 How can I render Django Form with vuetify?
📖 vue.js - Vuetifyの入力値でDjangoのテンプレートタグを使用する方法は?
📖 Anyone know how to use vuetify with django form?
📖 Source code for django.forms.boundfield
📖 DjangoのFormクラスを使う

User関連

📖 RelatedObjectDoesNotExist: User has no userprofile
📖 How to resolve the "psycopg2.errors.UndefinedTable: relation "auth_user" does not exist" when running django unittests on Travis - migrations/__init__.py は消してはダメ?

0
1
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
0
1