デジタル忍者ブログ

デジタル忍者ブログ

2025/04/07

Djangoでプロジェクト内にアプリを作成→カスタムユーザを定義する


使用環境
Windows11
python3.11
Django4.2

プロジェクトとアプリを作成します。


 > mkdir /path/to/kobushi
 > django-admin startproject kobushi .
 > mkdir user/accounts
 > python .\manage.py startapp accounts kobushi/user/accounts

accountsアプリをkobushiプロジェクトの中に生成します。
サブディレクトリ以降にアプリを作成する際は、そこまでのディレクトリが存在している必要があります。
そのため、事前にディレクトリを作成してからアプリを作成します。
次にsettings.pyにて設定を追加します。


# kobushi/settings.py

INSTALLED_APPS = [
    ...
    'kobushi.user.accounts', # add
    ...
]

AUTH_USER_MODEL = "accounts.User" 

ここで、AUTH_USER_MODELの定義について、
[app_label].[ModelName]
として決まっています。そのため、


AUTH_USER_MODEL = "kobushi.user.accounts.User" 

という定義方法はできません。この設定を行うと、createsuperuser等のコマンド実行時に次のエラーが表示されます。


ValueError: Invalid model reference 'kobushi.user.accounts.User'. String model references must be of the form 'app_label.ModelName'.

Djangoの定義によると、AppConfigの定義によって設定する値が決まるようです。

参考:https://docs.djangoproject.com/ja/5.1/ref/applications/

ーー
設定可能な属性
AppConfig.name
アプリケーションへの Python フルパス、たとえば 'django.contrib.admin' 。
この属性は設定がどのアプリケーションに適用されるかを定義します。
全ての AppConfig サブクラスで設定する必要があります。
Djangoプロジェクト全体の中で固有である必要があります。

AppConfig.label
アプリケーション用のショートネーム 例 'admin'
この属性により、2つのアプリケーションのラベルが競合している場合に、
アプリケーションのラベルを変更できます。デフォルトは name の最後のコンポーネントです。
これは有効な Python 識別子でなければなりません。
Djangoプロジェクト全体の中で固有である必要があります。
ーー


# kobushi/user/accounts/apps.py

class AccountsConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'kobushi.user.accounts'

INSTALLED_APPSの定義では'kobushi.user.accounts'を追加しているため、
apps.pyのnameと一致する必要があります。そして、この場合のlabelは'accounts'になります。
※上記の公式サイトにて"デフォルトは name の最後のコンポーネントです。"と記載されているので、
 ドットで区切った最後の文字列が該当します。

ちなみに、name = 'accounts' としてしまうと、createsuperuser等のコマンド実行時に次のエラーが表示されます。


django.core.exceptions.ImproperlyConfigured: Cannot import 'accounts'. Check that 'kobushi.user.accounts.apps.AccountsConfig.name' is correct.

次にカスタムユーザモデルを定義します。


# kobushi/user/accounts/models.py

from django.db import models
from django.contrib.auth.models import (BaseUserManager, AbstractBaseUser, PermissionsMixin)
from django.utils.translation import gettext_lazy as _

class UserManager(BaseUserManager):
  def _create_user(self, email, username, password, **extra_fields):
    email = self.normalize_email(email)
    user = self.model(email=email, username=username, **extra_fields)
    user.set_password(password)
    user.save(using=self._db)
    return user
  
  def create_user(self, email, username, password=None, **extra_fields):
    extra_fields.setdefault('is_active', True)
    extra_fields.setdefault('is_staff', False)
    extra_fields.setdefault('is_superuser', False)
    return self._create_user(email=email, username=username, password=password, **extra_fields)
  
  def create_superuser(self, email, username, password, **extra_fields):
    extra_fields['is_active'] = True
    extra_fields['is_staff'] = True
    extra_fields['is_superuser'] = True
    return self._create_user(email=email, username=username, password=password, **extra_fields)
  

class User(AbstractBaseUser, PermissionsMixin):

  username = models.CharField(
    verbose_name=_("username"), unique=True, max_length=150
  )
  email = models.EmailField(
    verbose_name=_("email"), unique=True
  )
  nick_name = models.CharField(
    verbose_name=_("nick_name"), max_length=150, null=True, blank=False
  )
  first_name = models.CharField(
    verbose_name=_("first_name"), max_length=150, null=True, blank=False
  )
  last_name = models.CharField(
    verbose_name=_("last_name"), max_length=150, null=True, blank=False
  )
  avator = models.ImageField(
    verbose_name=_("avators"), upload_to='avators', null=True, blank=True
  )
  profile = models.CharField(
    verbose_name=_("profile"), max_length=1000, null=True, blank=True
  )
  is_superuser = models.BooleanField(
    verbose_name=_("is_superuer"), default=False
  )
  is_staff = models.BooleanField(
    verbose_name=_('staff status'), default=False,
  )
  is_active = models.BooleanField(
    verbose_name=_('active'), default=True,
  )
  regtm = models.DateTimeField(
    verbose_name=_("regtm"), auto_now_add=True
  )
  modtm = models.DateTimeField(
    verbose_name=_("modtm"), auto_now=True
  )

  objects = UserManager()

  USERNAME_FIELD = 'username' 
  REQUIRED_FIELDS = ['email'] 

あとは、


> python manage.py makemigrations
> python mange.py migrate

にて、カスタムモデルユーザのデータベーステーブルが作成されます。

どうも、AbstractBaseUserを継承してカスタムユーザモデルの定義を行うにあたり、
usernameの定義が必要になっているようです。

参考にしたサイトだと、username定義せず、
代わりにaccount_idをUSERNAME_FIELDに設定しているので、
通るはずなんですけどね...

参考にしたサイトの定義で進めると、createsuperuser等のコマンド実行時に次のエラーが表示されました。
理由は・・・謎です。


SystemCheckError: System check identified some issues:

ERRORS:
: (admin.E033) The value of 'ordering[0]' refers to 'username', which is not a field of 'accounts.User'.
: (admin.E108) The value of 'list_display[0]' refers to 'username', which is not a callable, an attribute of 'UserAdmin', or an attribute or method on 'accounts.User'.

参考サイト:
https://qiita.com/ryo-keima/items/e6ce42bb4c11432ea829

Comment Form

コメント内容(必須)

Comment

現在、コメントはありません。