Djangoで会員登録機能を自作するシリーズ

2018-10-19 / PythonDjangoシリーズ・まとめ

概要

Djangoで、会員登録機能を自作していきます。メールアドレスをユーザー名として使うようにし、ログイン画面、仮登録、メールクリックで本登録、ユーザー情報変更ページ、パスワード変更ページ、パスワードを忘れた際の再設定...などなど、よくある一連の機能を実装します。動作環境はDjango2.2、Python3.5以上です。

Githubにソースコードを置いているので、ダウンロードしたい方はクローンしてください。

インストールして...

git clone https://github.com/naritotakizawa/django-register-sample
pip install django

すぐに試せます。

python manage.py migrate
python manage.py runserver
python manage.py createsuperuser

Userモデルのカスタマイズ

Djangoに用意されているUserモデルは、メールアドレスは単なるおまけのフィールドです。今回はメールアドレスをユーザー名として使えるようにしたいので、デフォルトのUserモデルをカスタマイズする必要があります。

Djangoで、Userモデルのカスタマイズ(継承)

ログイン画面

Djangoにもログイン画面は付属していますが、自分のウェブサイトのイメージと違う場合は自作することになります。

Djangoでログイン画面を自作する

ユーザー登録

ユーザー登録機能と、仮登録後にメールが届き、それにアクセスさせると本登録する、といった機能を実装します。

Djangoでユーザー作成処理(仮登録後、URLクリックで本登録)

マイページ

ユーザー情報の閲覧、更新ページです。閲覧と更新ページには、自分とスーパーユーザー以外はアクセスできないようにもします。

Djangoで、ユーザー情報閲覧・更新ページの作成

パスワード変更、パスワード忘れ

パスワードの変更ページと、パスワードを忘れた際の再設定ページを作ります。

Djangoで、パスワード変更ページと忘れた際の再設定ページ

メールアドレスの変更

メールアドレスを変更したい場合にも、会員登録時と同じように、確認メールを送ってリンクを踏むと変更されるようにします。

Djangoで、メールアドレス変更ページの作成

Tips

会員登録に関して、幾つかのTipsです。

入力内容の確認画面を作るには

素直に実装していくと、入力画面→確認画面 へどうやって入力データを渡し、確認画面からはどうやって前画面で入力されたデータを送信するのかについてを考えることになります。

色々なアプローチがありますが、Djangoの機能だけで確認画面のページを作りたい場合の例を紹介します。

Djangoで、フォームの内容を保持する(Context編)
Djangoで、フォームの内容を保持する(セッション編)

OneToOneの方が良さそうなケース

Userモデルのカスタマイズは強力ですが、OneToOneで別モデルと紐づけるほうが便利なこともあります。

この記事の関連記事

DjangoでUserモデルのカスタマイズ

2018-10-21 / PythonDjango

- Djangoで、会員登録機能を自作するシリーズの1つです。デフォルトのUserモデルでは、usernameというフィールドがありますが、今回はusernameをなくし、emailをメインに扱うUserモデルを作成します。

Djangoでログイン画面を作成する

2018-10-21 / PythonDjangoBootstrap4

- Djangoで、会員登録機能を自作するシリーズの1つです。Djangoにログイン画面は付属していますが、自分のウェブサイトのイメージと違う場合は自作することになります。Bootstrap4を使い、自作していきます。

Django、ユーザー登録ページとメールでのアクティベーション

2018-10-21 / PythonDjangoBootstrap4

- Djangoで、会員登録機能を自作するシリーズの1つです。ユーザー登録機能と、仮登録後にメールが届き、それにアクセスさせると本登録する、といった機能を実装します。

Djangoで、ユーザー情報閲覧・更新ページ

2018-10-21 / PythonDjangoBootstrap4

- Djangoで、会員登録機能を自作するシリーズの1つです。ユーザー情報の閲覧、更新ページを作ります。また、閲覧と更新ページには、自分とスーパーユーザー以外はアクセスできないようにもします。

Djangoで、パスワード変更ページと再設定ページ

2018-10-21 / PythonDjangoBootstrap4

- Djangoで、会員登録機能を自作するシリーズの1つです。パスワードの変更ページと、パスワードを忘れた際の再設定ページを作ります。

フォームの内容を保持する(テンプレートファイルに変数を渡す方法)

2018-10-21 / PythonDjango

- Djangoで、会員登録機能を自作するシリーズの1つです。Djangoで、ユーザー情報確認画面を作ります。ユーザー情報に限らず、フォームオブジェクトを保持しておく必要があったり、ロジック上1つ、2つ先のビューにフォームを渡す場合にも活用できます。

Djangoで、フォームの内容を保持する(セッションを使う方法)

2018-10-22 / PythonDjango

- Djangoで、会員登録機能を自作するシリーズの1つです。ユーザー情報の入力後に確認画面を表示したいと思います。ユーザー情報が入ったPOSTデータをセッションに保存する方法を使いますが、中々に便利です。

Django、Userモデルのカスタマイズ(OneToOne)

2018-10-22 / PythonDjango

- Djangoで、会員登録機能を自作するシリーズの1つです。Djangoのユーザーモデルを拡張します。今回はOneToOneでUserにProfile等のモデルを紐づけ、機能を追加していきます。

Django、メールアドレス変更ページの作成

2019-04-21 / PythonDjango

- Djangoで、会員登録機能を自作するシリーズの1つです。メールアドレスの変更ページを作ります。会員登録時と同じように、アクティベーションメールを使ってメールアドレスを変更できるようにします。

コメント欄

記事にコメントする

名無し

このアプリケーションを実行後、ログイン画面からユーザを登録、送信ボタンを押した後、エラーが発生してしまいました。 私だけでしょうか?

コメントに返信する

名無し

私も同様にログイン画面からユーザを登録、送信ボタンを押した後、エラーが発生してしまいました。 requestが定義されていないというエラーが出ましたが、requestは自動で取得されるかと思ったので、不思議でしたなぜなんでしょうか。

コメントに返信する

なりと

Djangoでユーザー作成処理(仮登録後、URLクリックで本登録)にミスタイプがありました。

UserCreateビューの'protocol': request.scheme,を、'protocol': self.request.scheme,と修正すれば動くと思います。

大学生

メールアドレスの登録を必須にしたのですが、ログインなどはユーザーネームで行うようにカスタマイズしました。 そうすると、スーパーユーザーを作る時にメールアドレスが必須となってしまい、エラーを吐いてしまいます。 スーパーユーザーを作るために、何か解決法がありましたら、教えていただきたいです。 よろしくお願いします。

コメントに返信する

大学生

REQUIRED FIELDにemailを追加し、解決しました。

渡辺

大変有用なサンプルを公開していただきありがとうございます。

Githubのソースで1点。 register/views.py の141行目の内容が欠けています。本サイトのページには存在する以下の行です。

141 success_url = reverse_lazy('register:password_change_done')

原因を見つけるのにsite-packagesの中まで追ってしまい手こずりました(^^;

コメントに返信する

なりと

ご連絡ありがとうございます、修正しておきます。

名無し

djangoの初心者で大変参考になりました。

twitterなどのSNS連携の記事を書いていただければ非常にありがたいです。 こちらの記事を参考に、カスタムユーザーを作ったのですが、 twitterとの連携ができずに悩んでいます。

もし可能であればぜひよろしくお願いいたします。

コメントに返信する

なりと

時間があれば、ぜひ取り上げたいと思います。

名無し

基本的にはここと同じように進めたのですが、createsuperuserをした後にadminにログインできず、operationalerror at admin/login no such table django_session と出てしまいます。どうすればsuperuserを登録できるのか教えてください。

コメントに返信する

なりと

migrateが正しく行われていないと思われます。一度データベースを削除し、再度migrateコマンドを実行してください。

名無し

migrateしなおした後にcreatesuperuserしようとすると、You have 3 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, sessions. Run 'python manage.py migrate' to apply them.と出ます。

なりと

Djangoのバージョンを上げて、もう一度db.sqlite3削除→migrateを行ってください。

なりと

解決しない場合、情報が足りないので使用しているDjangoやPythonのバージョン、どのOSかを教えてください。また、可能であればDjangoプロジェクトを送付してください。

名無し

最新版にアップデートしたところ、治りました。ありがとうございました。

名無し

Userモデルのカスタマイズで示されているカスタムユーザーモデルにusernameとuserIDのフィールドを追加して、 (class AbstractUserにuserID)を加えた状態でmigrationを行うと、以下のようなエラーを吐き出してしまうのですが、 何か解決策等ございませんか。

File "C:\Users\ーーー\AppData\Local\Programs\Python\Python37\lib\site-packages\django\db\models\options.py", line 566, in get_field raise FieldDoesNotExist("%s has no field named '%s'" % (self.object_name, field_name)) django.core.exceptions.FieldDoesNotExist: User has no field named 'username'

ここで質問するような内容でないのかもしれませんが、もしわかればよろしくお願いします。

コメントに返信する

なりと
    @property
    def username(self):
            ...

の部分を削除して、もう一度試してください。

名無し

お世話になっております。Djangoの勉強をし始めて、二ヶ月くらいたちますがいつも参考にさせていただいております。 一点、会員登録機能において、メール送信する際(新規登録時、メールアドレス変更時)に添付のエラーが出ます。 BadHeaderError subject.txtの実装も特に問題は無さそうなので何が原因わからず困っているので、解決策を共に考えてくださると幸いです。 何卒よろしくお願い申し上げます。

コメントに返信する

名無し

申し訳ございません、添付できておりませんでした。 エラー内容、以下になります。

名無し

BadHeaderError at /hr/register/email/change/ Header values can't contain newlines (got '会員登録まであともう少しです。\n' for header 'Subject')

なりと

can't contain newlines とあります。nelinesは改行を意味します。subject.txtの末尾に改行があるので、削除してください。件名には改行をいれれないのです。

名無し

ありがとうございます。Atomの設定で自動で最終行が改行されるようになっていたみたいです。 設定を変更したら無事通りました。

名無し

有用な記事をありがとうございます。

完成品をcloneし、migrateしたのですが、以下のエラーが出ております。

Traceback (most recent call last):
  File "manage.py", line 15, in <module>
    execute_from_command_line(sys.argv)
  File "C:\Users\ALEX\AppData\Local\Continuum\Anaconda3\envs\py36\lib\site-packages\django\core\management\__init__.py", line 381, in execute_from_command_line
    utility.execute()
  File "C:\Users\ALEX\AppData\Local\Continuum\Anaconda3\envs\py36\lib\site-packages\django\core\management\__init__.py", line 375, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "C:\Users\ALEX\AppData\Local\Continuum\Anaconda3\envs\py36\lib\site-packages\django\core\management\base.py", line 316, in run_from_argv
    self.execute(*args, **cmd_options)
  File "C:\Users\ALEX\AppData\Local\Continuum\Anaconda3\envs\py36\lib\site-packages\django\core\management\base.py", line 353, in execute
    output = self.handle(*args, **options)
  File "C:\Users\ALEX\AppData\Local\Continuum\Anaconda3\envs\py36\lib\site-packages\django\core\management\base.py", line 83, in wrapped
    res = handle_func(*args, **kwargs)
  File "C:\Users\ALEX\AppData\Local\Continuum\Anaconda3\envs\py36\lib\site-packages\django\core\management\commands\migrate.py", line 82, in handle
    executor = MigrationExecutor(connection, self.migration_progress_callback)
  File "C:\Users\ALEX\AppData\Local\Continuum\Anaconda3\envs\py36\lib\site-packages\django\db\migrations\executor.py", line 18, in __init__
    self.loader = MigrationLoader(self.connection)
  File "C:\Users\ALEX\AppData\Local\Continuum\Anaconda3\envs\py36\lib\site-packages\django\db\migrations\loader.py", line 49, in __init__
    self.build_graph()
  File "C:\Users\ALEX\AppData\Local\Continuum\Anaconda3\envs\py36\lib\site-packages\django\db\migrations\loader.py", line 273, in build_graph
    raise exc
  File "C:\Users\ALEX\AppData\Local\Continuum\Anaconda3\envs\py36\lib\site-packages\django\db\migrations\loader.py", line 247, in build_graph
    self.graph.validate_consistency()
  File "C:\Users\ALEX\AppData\Local\Continuum\Anaconda3\envs\py36\lib\site-packages\django\db\migrations\graph.py", line 243, in validate_consistency
    [n.raise_error() for n in self.node_map.values() if isinstance(n, DummyNode)]
  File "C:\Users\ALEX\AppData\Local\Continuum\Anaconda3\envs\py36\lib\site-packages\django\db\migrations\graph.py", line 243, in <listcomp>
    [n.raise_error() for n in self.node_map.values() if isinstance(n, DummyNode)]
  File "C:\Users\ALEX\AppData\Local\Continuum\Anaconda3\envs\py36\lib\site-packages\django\db\migrations\graph.py", line 96, in raise_error
    raise NodeNotFoundError(self.error_message, self.key, origin=self.origin)
django.db.migrations.exceptions.NodeNotFoundError: Migration register.0001_initial dependencies reference nonexistent parent node ('auth', '0011_update_proxy_permissions')

どのように対応すればよいかお教えいただけますと幸いです。

コメントに返信する

なりと

django2.2以上で試していただけますか。

名無し

django version 2.1.2 を使用していました。 2.2.22.2にしたところ、問題なく動作できました。 ありがとうございます。