3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【django-allauth対応】CustomUser登録時にProfileを自動生成する方法3選

Posted at

はじめに

Djangoでは、ユーザー登録後にそのユーザー専用のプロフィール情報を管理したいケースが多くあります。

例えば、CustomUserモデルとProfileモデルが1対1の関係にあり、ユーザーログイン後にプロフィール情報を含むトップページにアクセスしたとしましょう。
このとき、CustomUserに紐づくProfileオブジェクトが存在しないため、RelatedObjectDoesNotExistエラーが発生してしまいます。
管理画面でユーザーごとにプロフィールを手動で登録することもできますが、運用を考えると現実的ではありません。
そこで本記事では、アダプタを使わずに CustomUserモデルと1対1で紐づいたProfileモデルを、ユーザー登録時に自動生成する方法を3つ紹介します。

対象読者と実行環境

🎯 対象読者

  • ユーザー登録時にプロフィール情報を自動生成したい方
  • django-allauthを使ってログイン・サインアップを実装している方

🧪 実行環境

  • Python: 3.13.2
  • Django: 4.2.19
  • django-allauth: 65.4.1

🔧 前提条件

本記事は以下のような構成を前提としています。

  • Djangoの CustomUser モデルを使用している
    AbstractUserAbstractBaseUser を継承して独自に定義している)

  • CustomUser モデルと Profile モデルが1対1のリレーション(OneToOneField)で結びついている

  • プロフィール情報(アカウント名や自己紹介文など)は別モデル(Profile)で管理したい

  • django-allauth を導入している(任意)

🧾 この記事の内容をざっくり読みたい方へ

実装の要点だけを知りたい方は、Zennのスクラップ を御覧ください。

1️⃣ models.pyに直接記述する方法

django標準のシグナル post_saveを使うことで、CustomUser 登録後に 自動的に Profileが生成されます。
実装コードは以下のとおりです。

accounts/models.py
from django.db.models.signals import post_save

from user_profile.models import Profile

(

# 最下部に追記
def post_user_created(sender, instance, created, **kwargs):
    if created:
        Profile.objects.create(user=instance)

post_save.connect(post_user_created, sender=CustomUser)

これによって、CustomUserの保存後にpost_user_created()関数が呼ばれ、対応する Profile オブジェクトが自動生成されます。

個人的には、models.py内で記述が完結するため、シンプルに実装できる方法だと思います。
しかしながら、モデルが複雑になってきた場合に、ファイルが肥大化して、コードの見通しが悪くなるおそれがあります。
責務を分けて保守性を高める方法として、次の signals.py を作成する方法があります。

2️⃣ signals.pyを使って責務分割する方法

まず、 accounts アプリ内に signals.py ファイルを新規作成します。
実装コードは以下のとおりです。

accounts/signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver

from accounts.models import CustomUser
from user_profile.models import Profile


@receiver(post_save, sender=CustomUser)
def create_user_profile(sender, instance, created, **kwargs):
    if created:
        Profile.objects.create(user=instance)

1️⃣の実装との主な違いは、@receiver デコレータを使っている点です。

このcreate_user_profile関数を有効にするためには、 accounts/apps.py内に次のようにコードを追記する必要があります。

accounts/apps.py
class AccountsConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'accounts'
    
    # 以下のコードを追記
    def ready(self):  # <- インデント注意
        import accounts.signals  # noqa: F401

def ready のインデントに注意してください(classの中に記述しないと正常に動作しません)。
また、最終行の # noqa: F401 は、Flake8を導入している場合に未使用のimportに対して出される警告(F401)を無視するためのものです。

これで、1️⃣と同様に CustomUser の保存後に、Profile オブジェクトが自動生成されるようになります。

さて、ソーシャルアカウントとの連携のため、django-allauth を導入している方も少なくないと思います。
実は、django-allauthにもシグナルが用意されていて、次に紹介する方法で実装可能です。

3️⃣ django-allauth の signalを活用する方法

前述のとおり、django-allauthにもシグナルが用意されています(ただし、公式を見ても、どのように実装するかわかりにくい素っ気ない内容です)。

今回のようなケースで使用するシグナルは、user_signed_up です。
先程の2️⃣で作成した accounts/signals.py の内容を次のように書き換えます。

accounts/signals.py
from allauth.account.signals import user_signed_up
from django.dispatch import receiver
from user_profile.models import Profile


@receiver(user_signed_up)
def create_user_profile(request, user, **kwargs):
    Profile.objects.create(user=user)

django-allauth のシグナルを使うとコードがスッキリします。
加えて、ソーシャルアカウントと連携している場合、ソーシャルアカウントに紐づいた情報も取得・活用できるというメリットがあります。

ただし、注意することが一つあります。

⚠️ 注意:この方法は createsuperuser や管理画面からのユーザー作成には反応しません

これは django-allauth の仕組みによるもので、残念ながら回避が難しい部分です。

番外編;2️⃣ + 3️⃣のハイブリッド

django-allauthのシグナルを使いつつ、createsuperuser や管理画面からのユーザー作成時にもProfileを自動生成したい場合、若干力技ですが、以下の実装コードを試すのも一つの手だと思います。

accounts/signals.py
from allauth.account.signals import user_signed_up
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.conf import settings
from user_profile.models import Profile

@receiver(user_signed_up)
def create_profile_allauth(request, user, **kwargs):
    if not hasattr(user, 'profile'):
        Profile.objects.create(user=user)

@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_profile_post_save(sender, instance, created, **kwargs):
    if created and not hasattr(instance, 'profile'):
        Profile.objects.create(user=instance)

これにより、通常のallauth経由のサインアップ、管理画面や createsuperuserのどちらでもプロフィールが自動生成されるようになります。
しかしながら、管理者サイト等での登録は運用者向けであり、django-allauthによるサインアップと双方に対応させることで、思わぬバグが発生することも考えられます。したがって、実装にあたっては十分検討する必要があると考えます。

さいごに

シグナルは、特定のイベントをきっかけに処理を自動化できる便利な仕組みですが、使いすぎるとコードの挙動が見えづらくなり、保守が難しくなることもあります。
そのため、限定的な用途でうまく活用するのがおすすめです。

まとめ

実装の簡便さ:1️⃣の方法

責務分割と保守性:2️⃣の方法

django-allauth利用:3️⃣の方法

状況に応じて適切な方法を選んでみてください。

参考記事

Django公式ドキュメント - シグナル
https://itc.tokyo/django/save-with-post-save-signal/
https://djangostart.com/158/
allauth - signals
https://itc.tokyo/django/use-allauth-signals/



気になる点などございましたら、コメント欄でお気軽にお知らせください。
3
4
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
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?