DjangoでWebアプリを作りたい【第3回 Modelの作成】
前回簡単なViewを作成しました。今回はWriting your first Django app, part 2の章を進めていきたいと思います。
データベースのセットアップ
mysite/settings.pyを開いて、データベースのセットアップを行います。Djangoのデフォルト設定ではSQLiteを使用するようになっています。SQLiteはPythonに含まれているため新規にインストールする必要はありません。もし他のデータベースを使用する場合、DATABASES'default'の以下の項目を書き換えて下さい。
- ENGINE - 'django.db.backends.sqlite3', 'django.db.backends.postgresql', 'django.db.backends.mysql', 'django.db.backends.oracle'など。その他はこちら
- NAME - データベース名を指定します。SQLiteを使用している場合、データベースへの絶対パスを記入します。
SQLite以外を使用している場合、USER, PASSWORD, HOSTを追加で記入する必要があります。 デフォルトは以下のとおりです。
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } }
次にmysite/settings.pyのTIME_ZONEを以下のように設定します。
TIME_ZONE = 'Asia/Tokyo'
次にmysite/settings.pyのINSTALLED_APPSを見てみましょう。INSTALLED_APPSには現在のDjangoインスタンスで有効化されているアプリケーションが書かれています。アプリケーションは複数のプロジェクトで使用することができ、配布をすることも出来ます。デフォルトでは以下のアプリケーションが有効になっています。
- django.contrib.admin – 管理者用サイト
- django.contrib.auth – 認証システム
- django.contrib.contenttypes – コンテンツタイプフレームワーク
- django.contrib.sessions – セッション管理フレームワーク
- django.contrib.messages – メッセージフレームワーク
- django.contrib.staticfiles – 静的なファイルを管理するフレームワーク
上記のアプリケーションは、少なくとも1つのデータベーステーブルを利用します。ではテーブルを作成しましょう。ここで以下のコマンドを実行します。
$python manage.py migrate
migrateコマンドはINSTALLED_APPSを探し、mysite/settings.pyの設定に従って必要なテーブルを作成します。
モデルを作る
次にモデルを定義していきます。今回のアプリケーションpollでは、QuestionとChoiceという2つのモデルを作成します。Questionには質問(question)と公開日(publication)を、Choiceには質問(text)と投票数(vote)を格納します。各Choiceは1つのQuestionに関係があるものとします。
Djangoでは上記の仕様を以下のように表すことが出来ます。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') class Choice(models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0)
各モデルはdjango.db.models.Modelのサブクラスで表現されています。それぞれのモデルにはクラス変数があり、データフィールドを表しています。
各フィールドは、Fieldクラスのインスタンスとして表現されています。(CharFieldは文字のフィールド、DateTimeFieldは日時のフィールド)また、django.db.models.fieldの第一引数に人間が解読可能な値を持たせることで、フィールドに名前をつけることも出来ます。(今回はpub_dateに設定しました)
Fieldクラスの中には引数を必ず必要とするものもあります。例えば今回利用したCharFieldはmax_lengthの指定が必須となっています。
Choiceクラスの中で利用しているForeignKey関数を利用して、リレーションを定義することができます。
モデルを有効にする
今まで作成したモデルを有効にします。その前にpollアプリケーションを作成したことを知らせるため、mysite/settings.pyのINSTALL_APPSに'polls.apps.PollsConfig'追記します。
INSTALLED_APPS = ( 'polls.apps.PollsConfig', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', )
このように書くことでDjangoがpollsの存在を知ることが出来ます。ここまでできたら以下のコマンドを実行します。
$python manage.py makeimgrations polls
出力
0001_initial.py: - Create model Choice - Create model Question - Add field question to choice
makemigrationsを実行することで、Djangoにmodelの変更を知らせるとともにmigrationを作成します。(migrationは変更点を保存しているファイルと考えれば良いのだろうか)
さていま作成されたmigrationファイルの内容をデータベースに反映させましょう(migrate)
次のコマンドで実行されるsqlを確認してみます。
$python manage.py sqlmigrate 0001
すると以下の様な出力を得られます。
BEGIN; -- -- Create model Choice -- CREATE TABLE "polls_choice" ( "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "choice_text" varchar(200) NOT NULL, "votes" integer NOT NULL ); -- -- Create model Question -- CREATE TABLE "polls_question" ( "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "question_text" varchar(200) NOT NULL, "pub_date" datetime NOT NULL ); -- -- Add field question to choice -- ALTER TABLE "polls_choice" RENAME TO "polls_choice__old"; CREATE TABLE "polls_choice"("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "choice_text" varchar(200) NOT NULL, "votes" integer NOT NULL, "question_id" integer NOT NULL REFERENCES "polls_question"("id")); INSERT INTO "polls_choice"("votes", "choice_text", "id", "question_id") SELECT "votes", "choice_text", "id", NULL FROM "polls_choice__old"; DROP TABLE "polls_choice__old"; CREATE INDEX "polls_choice_7aa0f6ee" ON "polls_choice"("question_id"); COMMIT;
のような出力がされます。ではmigrateしましょう。
$python manage.py migrat
出力は以下のとおり
Operations to perform: Apply all migrations: auth, contenttypes, sessions, polls, admin Running migrations: Rendering model states... DONE Applying polls.0001_initial... OK
マイグレーションのまとめ
DBのマイグレーションをまとめると、
となります。必要ならばsqlmigrateをしてもよいでしょう。
APIを触ってみる。
Pythonのシェルを起動して、APIを触ってみたいと思います。まずは以下のコマンドを実行します。
$python manage.py shell
はじめにdatabase APIを触ってみます。
>> from polls.models import Question, Choice # Pollは1つもなし >>> Question.objects.all() [] # 新たな質問の作成 >>> from django.utils import timezone >>> q = Question(question_text="What's new?", pub_date=timezone.now()) # 作成されたオブジェクトをデータベースに登録。saveは明示的に呼ばなければならない。 >>> q.save() # オブジェクトのIDを表示する。1と1Lは等価 >>> q.id 1 # 各カラムへのアクセス >>> q.question_text "What's new?" >>> q.pub_date datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>) # 値の変更 >>> q.question_text = "What's up?" >>> q.save() # データベースに含まれているすべてのオブジェクトを表示 >>> Question.objects.all() [<Question: Question object>]
ここで以下のカスタムメソッドを追記します。
@python_2_unicode_compatible class Question(models.Model): ..... def was_published_recently(self): return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
そして再びshellを実行します。
>>> from polls.models import Question, Choice # 先ほどとは変わってWhat's up?(__str__()で指定したもの)になる。 >>> Question.objects.all() [<Question: What's up?>] # idが1のものや、質問がWhatで始まるものを抽出。 >>> Question.objects.filter(id=1) [<Question: What's up?>] >>> Question.objects.filter(question_text__startswith='What') [<Question: What's up?>] # 2015年の質問を取り出す。 >>> from django.utils import timezone >>> current_year = timezone.now().year >>> Question.objects.get(pub_date__year=current_year) <Question: What's up?> # 指定したidのものがない場合 >>> Question.objects.get(id=2) Traceback (most recent call last): ... DoesNotExist: Question matching query does not exist. # プライマリーキーを指定して抽出 >>> Question.objects.get(pk=1) <Question: What's up?> # 先ほど追記した関数のテスト >>> q = Question.objects.get(pk=1) >>> q.was_published_recently() True # Questionに2つのChoiceを追加する。createメソッドを呼び出すと、新たなChoiceオブジェクトを作成しINSERTを行いQuestionからアクセス可能な集合に追加される。(ChoiceにはQuestionが外部キーとして設定されているため) >>> q = Question.objects.get(pk=1) # 今は1つもChoiceがない。 >>> q.choice_set.all() [] # 3つのチョイスをつくる。 >>> q.choice_set.create(choice_text='Not much', votes=0) <Choice: Not much> >>> q.choice_set.create(choice_text='The sky', votes=0) <Choice: The sky> >>> c = q.choice_set.create(choice_text='Just hacking again', votes=0) # 関連付けられたQuestionへのアクセス >>> c.question <Question: What's up?> # Choiceの集合をすべて表示 >>> q.choice_set.all() [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>] >>> q.choice_set.count() 3 >>> Choice.objects.filter(question__pub_date__year=current_year) [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>] # deleteで削除 >>> c = q.choice_set.filter(choice_text__startswith='Just hacking') >>> c.delete()