pyhaya’s diary

機械学習系の記事をメインで書きます

Djangoのチュートリアル 2 モデルを作成する

前回の記事で、Djangoのインストールと非常に簡単なViewの作り方まで説明しました。
pyhaya.hatenablog.com
今回は、これにモデルを加えてみたいと思います。具体的には、QuestionChoiceという2つのモデルを作って、pollsアプリケーションで扱うデータ用のデータベースを整備します。

この記事の内容の作業環境は

  • Python3.6.5
  • Django2.1.2

のようになっています

モデルとは

Djangoに限ったことではありませんが、アプリケーションではデータベースを必ずといっていいほど使います。ユーザーの入力やページの現在の状況などのデータを格納するためにデータベースは欠かせません。

データベースには様々な種類があります。例えば有名なものだとMySQLやPostgresなどがあります。
f:id:pyhaya:20181027202718p:plain
DjangoではModelというものを作成して、データベース構成を決定します。まず、データベースのテーブルを作ります。manage.pyがあるディレクトリに移動して次のコマンドを実行します。

$ python manage.py migrate

これを実行すると

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... 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 auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying sessions.0001_initial... OK

のような表示が出て、データベースにテーブルが作成されます。

モデルを作る

では、実際にモデルを作ってみます。前回作ったpollsディレクトリで作業を行います。

polls/models.py

from django.db import models

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

ここでQuestionクラスとChoiceクラスは、それぞれ質問とそれに対する回答を表すモデルです。それぞれmodels.Modelクラスを継承しているのが見て取れると思います。これがモデルのテンプレートです。では各モデルを1つ1つ見ていきます。

Questionクラス

このクラスは2つのフィールドquestion_textpub_dateを持っています。これらにはそれぞれCharFieldDateTimeFieldが紐づけられています。この「~Field」という名前のものを使うことによって各フィールドにどのような種類のフィールドを設定するか決定しています。例えば、

Field名 フィールドの種類
CharField 文字列
TextField> テキスト
IntegerField 数値
DateTimeField 日付

などがあります。Fieldには本当に多くの種類があります。ほかのフィールドが知りたい方は以下のサイトが非常に見やすいと思います。
opendata-web.site

CharFieldTextFieldはどう違うのかぱっと見分かりにくいですが、TextFieldはブログ本文など、より長い文章を考えるときに使います。今回はquestion_textは質問文なのでCharFieldを使い、pub_dateは質問が投稿された日付を示しているのでDateTimeFieldになっています。

ここで重要なのは、question_textpub_dateといった変数名はデータベースのフィールド名として使うということです。

Choiceクラス

ChoiceクラスはQuestionクラスと比べると複雑です。この複雑さの原因は一行目のForeignKeyです。これはこのクラスのQuestionクラスとの関係を示しています。「各質問には選択肢があるよ」ということだと考えていれば大丈夫でしょう。

しかし1つだけ考えなくてはいけないことがあります。それはモデル同士が「一対一」、「多対一」、「一対多」、「多対多」のどれに対応するかという点です。今回の場合には、「一対多」に対応します。なぜなら質問1つに対して選択肢は複数あるからです。ForeignKeyというのは普通は「多」に対応するモデル(今回の場合には選択肢)に書くことが多いです。(多対多の場合にはManyToManyFieldを使います)

ForeignKeyの役割はわかりました。ではその引数のon_deleteは何を表しているのでしょう。この値はForeignKeyで参照しているオブジェクトが削除されたときに対応するオブジェクトをどうするかを決めるパラメータです。このパラメータはDjango2系では必須になっています。選択肢には次のものがあります。

パラメータ 説明
models.CASCADE こちらも同様に削除する
models.PROTECT こちらは削除しない
models.SET_NULL nullオプションがTrueの時にフィールドをnullにする
models.SET_DEFAULT デフォルト値が設定されているならその値にする

on_deleteの振る舞いについては以下のサイトがわかりやすいです。
codelab.website

データベースにpollsで使うフィールドを作成する

モデルができたので、pollsアプリケーションで使うフィールドをデータベースに作成しましょう。その前に、このプロジェクトにpollsアプリケーションが存在することを教えてあげなければなりません。そのためには、一番上位のフォルダ、mysiteにpollsアプリケーションを登録します。

mysite/settings.py

#...
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'polls.apps.PollsConfig',    # <- この行を追加
]
#...

どのような値をセットするかわからない場合には、アプリケーションのフォルダ中のapps.py内を見ればわかります。今回の場合にはPollsConfigクラスが定義されています。

これでプロジェクトはpollsアプリケーションを認識してくれます。コマンドライン

$ python manage.py makemigrations polls

と入力します。すると

Migrations for 'polls':
  polls/migrations/0001_initial.py
    - Create model Choice
    - Create model Question
    - Add field question to choice

と表示されて「Questionモデル」、「Choiceモデル」、そして「questionとchoiceを対応させるフィールド」が作成されます。

makemigrationsだけでは実は、models.pyの内容をデータベースへ登録するための中間ファイルしか作成していません(これをマイグレーションファイルと呼びます)。実際にデータベースに登録するにはもうひと手間必要で、

$ python manage.py migrate 

これで完了です。

管理ユーザーを作成する

作ったモデルを管理するために、管理ユーザーを作ります。これには、コマンドライン

$ python manage.py createsuperuser

とします。好きなユーザー名とメールアドレス、パスワードを設定します。終わったらpython manage.py runserverでサーバーを起動して
http://127.0.0.1:8000/adminにアクセスすると、次のようなログイン画面が現れます。
f:id:pyhaya:20181028205232p:plain
設定したユーザー名とパスワードでログインすると、管理者画面に移行します。
f:id:pyhaya:20181028205708p:plain
ここまでの状態では、QuestionやChoiceといったモデルが管理者画面に反映されていません。反映させるためには、pollsディレクトリのadmin.pyを編集します。

polls/admin.py

from django.contrib import admin
from .models import Question

admin.site.register(Question)

再び管理者画面を見ると、ちゃんとpollsアプリケーションのモデルが登録されています。
f:id:pyhaya:20181028210250p:plain
QuestionのところをクリックするとQuestionが登録できます。試しに「What's up?」というquestion textで、時刻はtoday, nowを押してsaveするとQuestionが登録できます。しかし、Questionの一覧に今登録したものが登録されています...しかし表示が「Question object (1)」となっていて何のことかさっぱりわかりません。オブジェクトの表示方法を変えるのはクラスに__str__を定義すればよいのでした。Pythonの特殊メソッドの使い方がよく分からない場合には以下のサイトをどうぞ
基本的な特殊メソッドと__str__ - Python学習講座
では、models.pyの編集をします。

polls/models.py

from django.db import models

# Create your models here.
class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

    def __str__(self):        # <- 特殊メソッド__str__を定義、question_textが表示されるようにする
        return self.question_text

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

    def __str__(self):        # <- ついでにChoiceのほうも
        return self.choice_text

これで、Question一覧の画面(
http://127.0.0.1:8000/admin/polls/question/
)にはWhat's upと、qustion_textが表示されるようになっています。

今回はここまでです。この記事で書いたことは

  • データベースのマイグレーション方法
  • モデルの作り方とデータベースへの登録方法
  • 管理ユーザーの設定

でした。次回はこのモデルを使って実際のページの編集を行っていきます。

pyhaya.hatenablog.com