パート2
モデルを定義

モデルを定義する

出欠掲示板のようなデータベースにデータを保存するようなアプリケーションを作成するときには、データベースのテーブルと対応するモデルを定義する必要があります。

SQLを使ってデータベースを操作することもできますが、Djangoでは、データベースのテーブルと対応するモデルを定義することで、Pythonのコードを使ってデータベースを操作することができます。

データベースのテーブルは、データベースの中にある表のようなものです。データベースのテーブルには、行と列があります。行は、データベースのテーブルに保存されているデータの1つ1つを表します。列は、データベースのテーブルに保存されているデータの種類を表します。

モデルは、各アプリケーションディレクトリ内のmodels.pyファイルに定義します。

Userモデルの定義

まずは、ユーザー認証機能にも用いるUserモデルを定義します。

Userモデルにどういったフィールドが必要かを考えてみましょう。

  • ユーザー名
  • 氏名
  • メールアドレス
  • パスワード
  • 科類
  • 学部
  • 学科
  • 学年

くらいでしょうか。

これらのフィールドをUserモデルに定義していきます。

モデルの定義の際は、Djangoが提供しているクラスを継承して定義します。

一般的には、models.Modelを継承するのですが、Userモデルのようにユーザー認証機能にも用いるモデルの場合はパスワードのハッシュ化といった特殊な処理が必要になるため、AbstractBaseUserを継承した方がより簡単です。

参考:Customizing authentication in Django (opens in a new tab)

accounts/models.py
from django.contrib.auth.models import AbstractBaseUser
 
 
class User(AbstractBaseUser):
    pass  # ここにフィールドを定義していく

ここで、VSCodeでAbstractBaseUserにカーソルを合わせてCtrl+クリック(Macの場合は+クリック)を押して、AbstractBaseUserクラスの定義を確認してみましょう。

class AbstractBaseUser(models.Model):
    password = models.CharField(_("password"), max_length=128)
    last_login = models.DateTimeField(_("last login"), blank=True, null=True)
 
    is_active = True
 
    REQUIRED_FIELDS = []

これをみると、AbstractBaseUserクラスは、passwordlast_loginというフィールドを持っていることがわかります。

Userモデルはこれを継承しているので、Userモデルもpasswordlast_loginというフィールドを元から持っています。

accounts/models.py
from django.contrib.auth.models import (
    AbstractBaseUser,
    BaseUserManager,
    PermissionsMixin,
)
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.utils.translation import gettext_lazy as _
from django.utils import timezone
 
# Create your models here.
 
 
class User(AbstractBaseUser, PermissionsMixin):
    username = models.CharField(max_length=150, unique=True, verbose_name="ユーザー名")
    last_name = models.CharField(max_length=150, blank=True, verbose_name="姓")
    first_name = models.CharField(max_length=30, blank=True, verbose_name="名")
    email = models.EmailField(unique=True, verbose_name="メールアドレス")
 
    COURSE_CHOICES = (
        ("l1", "文科一類"),
        ("l2", "文科二類"),
        ("l3", "文科三類"),
        ("s1", "理科一類"),
        ("s2", "理科二類"),
        ("s3", "理科三類"),
    )
    course = models.CharField(max_length=2, choices=COURSE_CHOICES, verbose_name="科類")
 
    FACULTY_CHOICES = (
        ("law", "法学部"),
        ("med", "医学部"),
        ("eng", "工学部"),
        ("lit", "文学部"),
        ("sci", "理学部"),
        ("agr", "農学部"),
        ("eco", "経済学部"),
        ("art", "教養学部"),
        ("edu", "教育学部"),
        ("pha", "薬学部"),
    )
    faculty = models.CharField(
        max_length=3, choices=FACULTY_CHOICES, blank=True, verbose_name="学部"
    )
 
    department = models.CharField(max_length=150, blank=True, verbose_name="学科")
 
    GRADE_CHOICES = (
        ("B1", "1年生"),
        ("B2", "2年生"),
        ("B3", "3年生"),
        ("B4", "4年生"),
        ("B5", "5年生"),
        ("B6", "6年生"),
    )
    grade = models.CharField(max_length=2, choices=GRADE_CHOICES, default="B1", verbose_name="学年")
 
    # 班は後で追加
 
    # 期は後で追加(年によって動的に変わるため)
 
    is_staff = models.BooleanField(
        _("staff status"),
        default=False,
        help_text=_("Designates whether the user can log into this admin site."),
    )
    is_active = models.BooleanField(
        _("active"),
        default=True,
        help_text=_(
            "Designates whether this user should be treated as active. "
            "Unselect this instead of deleting accounts."
        ),
    )
    date_joined = models.DateTimeField(_("date joined"), default=timezone.now)
 
    USERNAME_FIELD = "username"
    REQUIRED_FIELDS = ["email"]

これで、Userモデルの定義は完了です。

ユーザー名とメールアドレスは一意である必要があるため、unique=Trueを指定しています。 これで、別のユーザーが同じユーザー名やメールアドレスを使って登録することを防ぐことができます。

科類と学部は選択式で実装しました。 この場合、choicesを指定することで、選択肢を定義することができます。 choicesには、iterableなオブジェクトを指定します(tuplelistなど)。

COURSE_CHOICESFACULTY_CHOICESのように、choicesに指定するiterableなオブジェクトは、各要素がtupleで、1つ目の要素がデータベースに保存される値、2つ目の要素がユーザーに表示される値となっています。

班と期については、後ほど追加することにしましょう。

is_staffis_activeは、管理画面にログインできるかどうかを判定するためのフラグです。 これは、Djangoが提供しているAbstractUserクラスにも定義されているフィールドです。 (ユーザーモデルのカスタマイズがそこまでいらない場合は、AbstractUserを継承するのが簡単です。)

USERNAME_FIELDは、ユーザー名として使うフィールドを指定します。

migrationsを作成する

モデルを定義したら、migrationsを作成します。

migrationsは、モデルの定義をもとに、データベースのテーブルを作成するためのファイルです。

migrationsを作成するには、makemigrationsコマンドを実行します。

python manage.py makemigrations

makemigrationsコマンドを実行すると、migrationsディレクトリ内に、モデルの定義をもとに作成されたmigrationsファイルが作成されます。

データベースのテーブルを作成する

次に、migrateコマンドを実行して、実際にデータベースのテーブルを作成します。

python manage.py migrate

これで、Userモデルに対応するデータベースのテーブルが作成されました。

エラーが出たら、その都度修正していきましょう。

CustomUserManagerを定義する

Userモデルを定義したら、CustomUserManagerを定義します。

CustomUserManagerは、ユーザー認証機能にも用いるUserモデルを操作するためのクラスです。

accounts/models.pyファイルを開いて、以下のように編集します。

accounts/models.py
from django.contrib.auth.models import BaseUserManager
 
 
class CustomUserManager(BaseUserManager):
    def create_user(self, username, email, password=None):
        if not email:
            raise ValueError("Users must have an email address")
 
        user = self.model(
            username=username,
            email=self.normalize_email(email),
        )
 
        user.set_password(password)
        user.save(using=self._db)
        return user
 
    def create_superuser(self, username, email, password):
        user = self.create_user(
            username=username,
            email=self.normalize_email(email),
            password=password,
        )
 
        user.is_staff = True
        user.is_active = True
        user.is_superuser = True
        user.save(using=self._db)
        return user
⚠️

これは、Userモデルを定義する前に記述しておく必要があります。

最後に、UserモデルにCustomUserManagerを設定します。

accounts/models.pyファイルを開いて、以下のように編集します。

accounts/models.py
class User(AbstractBaseUser):
    # 省略
 
    objects = CustomUserManager()

管理画面にUserモデルを追加する

Djangoは、非常に強力な管理画面をデフォルトで提供しています。

管理画面は、http://127.0.0.1/admin (opens in a new tab) からアクセスできます。

この管理画面ではデータベースに保存されているデータを編集したり、追加したりすることができるのですが、デフォルトではUserモデルが管理画面に表示されていません。

そこで、管理画面にUserモデルを追加していきます。

accounts/admin.pyファイルを開いて、以下のように編集します。

accounts/admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
 
from .models import User
 
 
@admin.register(User)
class CustomUserAdmin(UserAdmin):
    fieldsets = (
        (None, {"fields": ("username", "password")}),
        (
            "Personal info",
            {
                "fields": (
                    "last_name",
                    "first_name",
                    "email",
                    "course",
                    "faculty",
                    "department",
                    "grade",
                )
            },
        ),
        (
            "Permissions",
            {
                "fields": (
                    "is_active",
                    "is_staff",
                )
            },
        ),
        ("Important dates", {"fields": ("last_login", "date_joined")}),
    )
    list_display = (
        "username",
        "email",
        "last_name",
        "first_name",
        "is_staff",
    )
    search_fields = ("username", "email", "last_name", "first_name")
    ordering = ("username",)
 
    filter_horizontal = ()
    list_filter = ()

これで、管理画面にUserモデルが追加されました。

UserモデルをDjangoのデフォルトのユーザーモデルとして設定する

UserモデルをDjangoのデフォルトのユーザーモデルとして設定するには、settings.pyファイルを編集します。

settings.pyファイルを開いて、以下のように編集します。

mysite/settings.py
AUTH_USER_MODEL = "accounts.User"

これで、Django標準のユーザーモデルではなく、この団体での使用に適したUserモデルを使うことができるようになりました。

管理画面にログインできるようにする

管理画面にログインできるようにするには、superuserを作成する必要があります。

superuserは、管理画面にログインできるユーザーのことです。

superuserを作成するには、createsuperuserコマンドを実行します。

python manage.py createsuperuser

createsuperuserコマンドを実行すると、ユーザー名、メールアドレス、パスワードを聞かれるので、入力してください。

ユーザー名:
メールアドレス:
Password:
Password (again):
Superuser created successfully.

これで、管理画面にログインできるようになりました。

http://127.0.0.1/admin (opens in a new tab) にアクセスして、ログインしてみましょう。

あなたが今定義したUserモデルが管理画面に表示されていることが確認できると思います。


モデルを定義するだけでこんなに大変なのかと思ったかも知れません。

でも安心してください。 これは特殊なケースで、Userモデルはユーザー認証機能にも用いるからです。

Userモデルの実装ができるのならば、他のモデルの実装はとても簡単に感じるでしょう。