らっちゃいブログ

日々の学びと気づきを発信するブログ

Djangoで認証ユーザーモデルをカスタマイズする

スポンサーリンク

Djangoで認証を行ったりした際に得られる User オブジェクトですが、デフォルトのままだと不要なデータが多かったり、必要なフィールドがなかったりします。

そんなときのために、今回はデフォルトのモデルである django.contrib.auth.models.Userをカスタマイズして、新しい User モデルを作成する方法について解説します。

プロジェクトを作成

お決まりの racchai プロジェクトを作成します。

$ django-admin startproject racchai

django のインストールがまだという方はこちらを参考にインストールしておきましょう。

racchai.hatenablog.com

プロジェクトが作成できたら、racchai プロジェクトをINSTALLED_APPS に追加しておきます。

# Application definition

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
+   'racchai',
)

プロジェクトの作成はこれでおしまいです。

データベースの設定

racchai/settings.pyを見てみましょう。

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

初期設定では sqliteを使うことになっていますね。本記事では MySQL を使って進めていきますので、MySQL を使うように設定を変更しておきます。

mysql のユーザー名およびパスワードについては自身の環境にあわせて置き換えてください。

DATABASES = { 
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'racchai',
        'USER': 'root',
        'PASSWORD': 'root',
        'HOST': 'localhost',
    }   
}

上記の設定で、ローカルにインストールされた MySQL の racchai というデータベースを参照するようになりました。

さっそくデータベースとテーブルを作成してみましょう。

っとその前に、まだ python から MySQL へアクセスするためのドライバをインストールしていませんので、インストールしておく必要があります。

$ pip install MySQL-python

これで準備が整いましたので、データベースとテーブルを作成します。

$ mysql -uroot -p -e "CREATE DATABASE racchai";
$ ./manage.py migrate
Operations to perform:
  Synchronize unmigrated apps: staticfiles, messages
  Apply all migrations: admin, contenttypes, auth, sessions
Synchronizing apps without migrations:
  Creating tables...
    Running deferred SQL...
  Installing custom SQL...
Running migrations:
  Rendering model states... DONE
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... 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 sessions.0001_initial... OK

はい、作成されました。

ではユーザーモデルに対応するテーブル情報を見てみましょう。

$ mysql -uroot -p racchai
mysql> desc auth_user;
+--------------+--------------+------+-----+---------+----------------+
| Field        | Type         | Null | Key | Default | Extra          |
+--------------+--------------+------+-----+---------+----------------+
| id           | int(11)      | NO   | PRI | NULL    | auto_increment |
| password     | varchar(128) | NO   |     | NULL    |                |
| last_login   | datetime(6)  | YES  |     | NULL    |                |
| is_superuser | tinyint(1)   | NO   |     | NULL    |                |
| username     | varchar(30)  | NO   | UNI | NULL    |                |
| first_name   | varchar(30)  | NO   |     | NULL    |                |
| last_name    | varchar(30)  | NO   |     | NULL    |                |
| email        | varchar(254) | NO   |     | NULL    |                |
| is_staff     | tinyint(1)   | NO   |     | NULL    |                |
| is_active    | tinyint(1)   | NO   |     | NULL    |                |
| date_joined  | datetime(6)  | NO   |     | NULL    |                |
+--------------+--------------+------+-----+---------+----------------+

いろいろ定義されていますね。次はいよいよカスタマイズする方法です。

RacchaiUserモデルを作成する

今回は以下のようなモデルを作成してみます。

  • テーブル名を racchai_user とする
  • ログイン名として email フィールドを持つ
  • twitter の URL を持つ

ではやってみます。

racchai/models.py というファイルを作り、その中で RacchaiUser クラスを定義しましょう。

from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager

class RacchaiUserManager(BaseUserManager):
    def create_user(self, email, password=None, **extra_fields):
        if not email:
            raise ValueError('Users must have a email address')
        email = RacchaiUserManager.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, password):
        return self.create_user(email, password)

class RacchaiUser(AbstractBaseUser):
    email = models.EmailField(max_length=128, unique=True)
    twitter_url = models.URLField()

    USERNAME_FIELD = 'email'

    objects = RacchaiUserManager()

    class Meta:
        db_table = 'racchai_user'
        swappable = 'AUTH_USER_MODEL'

RacchaiUser を作成すると同時に RacchaiUserManager も作成していますが、ユーザーモデルをカスタマイズする際はこれらをセットで作成する必要があるということだけ覚えておいてもらえれば大丈夫です。

次は、Django が参照しているモデルクラスを差し替えます。racchai/settings.py にて以下を追記してください。

AUTH_USER_MODEL = 'racchai.RacchaiUser'

できましたか?

たったこれだけでカスタマイズしたUserモデルに差し替えることができています。マイグレーションして確認してみましょう。

$ ./manage.py makemigrations racchai
Migrations for 'racchai':
  0001_initial.py:
    - Create model RacchaiUser

$ ./manage.py migrate
Operations to perform:
  Synchronize unmigrated apps: staticfiles, messages
  Apply all migrations: admin, contenttypes, sessions, auth, racchai
Synchronizing apps without migrations:
  Creating tables...
    Running deferred SQL...
  Installing custom SQL...
Running migrations:
  Rendering model states... DONE
  Applying racchai.0001_initial... OK
The following content types are stale and need to be deleted:

    auth | user

Any objects related to these content types by a foreign key will also
be deleted. Are you sure you want to delete these content types?
If you're unsure, answer 'no'.

    Type 'yes' to continue, or 'no' to cancel: yes

マイグレーションは無事成功です。次は肝心のテーブル情報を見てみましょう。

$ mysql -uroot -p racchai
mysql> desc racchai_user;
+-------------+--------------+------+-----+---------+----------------+
| Field       | Type         | Null | Key | Default | Extra          |
+-------------+--------------+------+-----+---------+----------------+
| id          | int(11)      | NO   | PRI | NULL    | auto_increment |
| password    | varchar(128) | NO   |     | NULL    |                |
| last_login  | datetime(6)  | YES  |     | NULL    |                |
| email       | varchar(128) | NO   | UNI | NULL    |                |
| twitter_url | varchar(200) | NO   |     | NULL    |                |
+-------------+--------------+------+-----+---------+----------------+

racchai_user テーブルが作成されており、中身はさきほど RacchaiUser クラスで定義したものになっていることがわかりますね。

動作確認

では実際にDjangoが参照するユーザーモデルが差し替わっているのか確認してみます。

ユーザー作成

createsuperuser コマンドで racchai_user テーブルにデータが登録されるかを確認してみます。

$ ./manage.py createsuperuser
Email: test@racchai.com
Password: test
Password (again): test
Superuser created successfully.

$ mysql -uroot -p racchai -e "SELECT * FROM racchai_user"
+----+-------------------------------------------------------------------------------+------------+---------------+-------------+
| id | password                                                                      | last_login | email         | twitter_url |
+----+-------------------------------------------------------------------------------+------------+---------------+-------------+
|  1 | pbkdf2_sha256$20000$MlRFZRiDro4P$lIhUqpljqcqWDiaj0AnCTB8Y2NWBE5wempF31hY6jws= | NULL       | test@racchai.com |             |
+----+-------------------------------------------------------------------------------+------------+---------------+-------------+

入りました!

認証

ユーザー認証の結果として得られるユーザーオブジェクトが差し替わっているかを確認します。

$ ./manage.py shell
>>> from django.contrib.auth import authenticate
>>> authenticate(email='test@racchai.com', password='test')
<RacchaiUser: test@racchai.com>

無事認証結果が RacchaiUser オブジェクトになってますね!

まとめ

今回はユーザーモデルをカスタマイズする方法についてご紹介してみました。

多少手間ではありますが、サービスに合ったユーザーモデルにカスタマイズするだけで開発効率が全然違ってくると思いますので、ぜひ試してみてください。