Skip to content

2.モデルと管理サイト#

はじめての Django アプリ作成、その2

Database の設定#

  • Django はデフォルトの設定で SQLite を使用する
  • 他のデータベースを使いたい場合、データベースのバインディングをインストールして、設定ファイルの DATABASES の default 項目内の以下のキーをデータベースの接続設定に合うように変更する

  • ENGINE

  • django.db.backends.sqlite3
  • django.db.backends.postgresql
  • django.db.backends.mysql' またはdjango.db.backends.oracle`
  • その他のバックエンドも利用可能
  • NAME
  • データベースの NAME です。SQLiteを使用している場合、データベースはコンピュータ上のファイルになる
  • デフォルト値の BASE_DIR / db.sqlite3 は、プロジェクトディレクトリにファイルを保存する

Info

  • データベースとして SQLite を使っていない場合、 USER や PASSWORD そして HOST などの追加設定を加える必要がある
  • SQLite 以外を使っている場合、 database を今のうちに作成しておくこと。
  • CREATE DATABASE database_name; とデータベースのインタラクティブプロンプトで実行する
  • 詳細については DATABASESのリファレンスドキュメントを参照。

settings.py#

mysite/settings.py ファイル内の先頭に記述してあるINSTALLED_APPSはDjangoインスタンスの中で有効化されているすべてのDjangoアプリケーションの名前が 記述されている。デフォルトでは以下のアプリケーションが入っている。

  • django.contrib.admin - 管理(admin)サイト。まもなく使います
  • django.contrib.auth - 認証システム
  • django.contrib.contenttypes - コンテンツタイプフレームワーク
  • django.contrib.sessions - セッションフレームワーク
  • django.contrib.messages - メッセージフレームワーク
  • django.contrib.staticfiles - 静的ファイルの管理フレームワーク

これらのアプリケーションは最低1つデータベースのテーブルを使うため、使い始まる前にデータベースにテーブルを作る必要がある。 python manage.py migrateを実行してデータテーブルを作成。

(venv) C:\Users\omron\Documents\20_Python\dev\Django\mysite> 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 auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying sessions.0001_initial... OK
(venv) C:\Users\omron\Documents\20_Python\dev\Django\mysite> 

モデルの作成#

チュートリアルの poll アプリケーションでは、Question と Choice の2つのモデルを作成。 Poll には question と publication date の情報があり、 Choice には選択肢のテキストと vote という2つのフィールドがある。各 Choice は1つの Question に関連づけられている。 polls/models.py ファイルを以下のように編集する。ss

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)
  • 各モデルは一つのクラスで表現され、いずれも django.db.models.Model のサブクラス
  • こうしたクラスは、各フィールドにどのようなデータ型を記憶させるか を Django に指示する
  • 各モデルには複数のクラス変数があり、個々のクラス変数はモデルのデータベースフィールドを表現
  • 各フィールドは Field クラスのインスタンスとして表現
  • Field インスタンスそれぞれの名前(例: question_text や pub_date)は、機械可読なフィールド名
  • このフィールド名はPythonコードで使うとともに、データベースも列の名前として使う
  • CharField: 文字のフィールド
  • DateTimeField: 日時フィールド
  • ForeignKey:リレーションシップを定義。れぞれの Choice が一つの Question に関連付けられることを Django に伝える。

モデルを有効にする#

  • アプリケーションをプロジェクトに含めるには、構成クラスへの参照を INSTALLED_APPS 設定に追加する
  • PollsConfig クラスは、 polls/apps.py にあるので、ドットつなぎのパスは polls.apps.PollsConfig となる。
  • mysite/settings.py を編集し、 INSTALLED_APPS 設定にドットつなぎのパスを追加する
    mysite/settings.py
    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 makemigrations pollsを実行
    (venv) C:\Users\omron\Documents\20_Python\dev\Django\mysite> python manage.py makemigrations polls
    Migrations for 'polls':
      polls\migrations\0001_initial.py
        - Create model Question
        - Create model Choice
    
  • makemigrations を実行することで、Djangoにモデルに変更があったこと(この場合、新しいものを作成しました)を伝え、そして変更を マイグレーション の形で保存する。

  • Django には、マイグレーションを自動でデータベーススキーマを管理するためのmigrateコマンドがある

  • まずは、マイグレーションがどんなSQLを実行するのか見てみる
  • sqlmigrate コマンドはマイグレーションの名前を引数にとってSQLを返す:
    (venv) C:\Users\omron\Documents\20_Python\dev\Django\mysite> python manage.py sqlmigrate polls 0001
    BEGIN;
    --
    -- 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);
    --
    -- Create model Choice
    --
    CREATE TABLE "polls_choice" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "choice_text" varchar(200) NOT NULL, "votes" integer NOT NULL, "question_id" bigint NOT NULL REFERENCES "polls_question" ("id") DEFERRABLE INITIALLY DEFERRED);
    CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id");
    COMMIT;
    (venv) C:\Users\omron\Documents\20_Python\dev\Django\mysite>
    
  • 次に migrate を再度実行し、 モデルのテーブルをデータベースに作成しましょう。
    (venv) C:\Users\omron\Documents\20_Python\dev\Django\mysite> python manage.py migrate
    Operations to perform:
      Apply all migrations: admin, auth, contenttypes, polls, sessions
    Running migrations:
      Applying polls.0001_initial... OK
    (venv) C:\Users\omron\Documents\20_Python\dev\Django\mysite> 
    

Tip

manage.py でできることは「django-admin と manage.py」を参照

API をたたく#

python manage.py shellコマンドで Python シェルを起動

Info

"python" でpython シェル起動ではないのは、manage.py は DJANGO_SETTINGS_MODULE 環境変数を設定しているからです。

(venv) C:\Users\omron\Documents\20_Python\dev\Django\mysite> python manage.py shell
Python 3.9.0 (tags/v3.9.0:9cf6752, Oct  5 2020, 15:34:40) [MSC v.1927 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> 
>>> from polls.models import Choice, Question 
>>> Question.objects.all()
<QuerySet []>
>>> 
>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=timezone.now())
>>> q.save()
>>> q.id
1
>>> q.question_text
"What's new?"
>>> q.pub_date
datetime.datetime(2023, 1, 1, 14, 38, 37, 517769, tzinfo=datetime.timezone.utc)
>>> q.question_text = "What's up?"
>>> q.save()
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>
>>>

<Question: Question object (1)> は、このオブジェクトの表現としてまったく役に立たたない。 (polls/models.py ファイル内にある) Question モデルを編集してこれを修正する。 __str__() メソッドを Question と Choice の両方に追加

polls/models.py
from django.db import models

class Question(models.Model):
    # ...
    def __str__(self):
        return self.question_text

class Choice(models.Model):
    # ...
    def __str__(self):
        return self.choice_text

Djangoの自動生成adminでオブジェクトの表現として使用されるという理由からも __str__() メソッドをモデルに追加することは重要

また、モデルクラスに以下クラスメソッドを追加する。詳細はタイムゾーンサポートドキュメント を参照

polls/models.py
import datetime

from django.db import models
from django.utils import timezone


class Question(models.Model):
    # << snip >>
    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

変更を保存して、もう一度 python manage.py shell を実行して新しい Python 対話シェルを始める

(venv) C:\Users\omron\Documents\20_Python\dev\Django\mysite> python manage.py shell
Python 3.9.0 (tags/v3.9.0:9cf6752, Oct  5 2020, 15:34:40) [MSC v.1927 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from polls.models import Choice, Question
>>> Question.objects.all()
<QuerySet [<Question: What's up?>]>
>>> Question.objects.filter(id=1)
<QuerySet [<Question: What's up?>]>
>>> Question.objects.filter(question_text__startswith='What')
<QuerySet [<Question: What's up?>]>
>>>
>>> 
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Question.objects.get(pub_date__year=current_year)
<Question: What's up?>
>>> Question.objects.get(id=2)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "C:\Users\omron\Documents\20_Python\dev\Django\venv\lib\site-packages\django\db\models\manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "C:\Users\omron\Documents\20_Python\dev\Django\venv\lib\site-packages\django\db\models\query.py", line 650, in get
    raise self.model.DoesNotExist(
polls.models.Question.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
>>> q = Question.objects.get(pk=1)
>>> q.choice_set.all()
<QuerySet []>
>>> 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)
>>> c.question
<Question: What's up?>
>>> q.choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
>>> q.choice_set.count()
3
>>> Choice.objects.filter(question__pub_date__year=current_year)
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
>>> c = q.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()
(1, {'polls.Choice': 1})

Tip

Django Admin#

Info

Djangoはモデルのための管理インタフェース群の生成の自動化が用意されている

管理ユーザーを作成する#

adminサイトにログインできるユーザーを作成するにはpython manage.py createsuperuserを実行

(venv) C:\Users\omron\Documents\20_Python\dev\Django\mysite> python manage.py createsuperuser
Username (leave blank to use 'omron'): admin
Email address: admin@example.com
Password: 
Password (again):
The password is too similar to the username.
This password is too short. It must contain at least 8 characters.
This password is too common.
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.
(venv) C:\Users\omron\Documents\20_Python\dev\Django\mysite> 

開発サーバーの起動#

Django adminサイトはデフォルトで有効化される python manage.py runserverでサーバを起動し、ブラウザでhttp://127.0.0.1:8000/admin/と入力してadmin コンソールにアクセス。

admin サイト#

先ほど作成した管理ユーザでログインすると以下Django admin のインデックスページが表示される。

いくつかのタイプの編集可能なコンテンツがある(groups と users)。 これらは Django に含まれる認証フレームワーク django.contrib.authによって提供されます

Poll アプリを admin 上で編集できるようにする#

Question オブジェクトがadmin インタフェースを持つということを、adminに伝える必要があり、 これを行うために、ファイル polls/admin.py を開いて以下のように編集する

polls/admin.py
from django.contrib import admin

from .models import Question

admin.site.register(Question)

admin の機能#

先の手順で Question を登録したので、 Django は admin インデックスページにこれを表示することを知っている。

"Questions" をクリックすると、questions のための "change list" ページが表示される このページにはデータベース中のすべての question が表示され、その中のひとつを選んで変更することができる ここには以前作成した "What's UP?" question も表示されていることがわかる

"What's up?" questionを編集するためにクリック

以下の点に注意:

  • フォームは Question モデルから自動的に生成される。
  • モデルのフィールドの型 (DateTimeField 、 CharField など) はそれぞれ異なる HTML 入力ウィジェットと対応。各種のフィールドは、自分自身を Django admin サイトでどう表示するか知っている。
  • 各 DateTimeField は JavaScript ショートカットがついています。日付 (dates) のカラムには「今日 (today)」 へのショートカットとカレンダーポップアップボタンがあります。 時刻 (times) には「現在 (now)」へのショートカットと、よく入力される時刻のリストを表示するポップアップボタンがあります。

ページの末尾の部分には操作ボタンがいくつか表示されている:

  • 保存 (Save) – 変更を保存して、このモデルのチェンジリストのページに戻る。
  • 保存して編集を続ける (Save and continue editing) – 変更を保存して、このオブジェクトの編集ページをリロードします。
  • 保存してもう一つ追加 (Save and add another) – 変更を保存して、このモデルのオブジェクトを新規追加するための空の編集ページをロードします。
  • 削除 (Delete) – 削除確認ページを表示。

もし「Date published」の値があなたが以前 チュートリアルその1 で作成した questionと一致しないのであれば、 それはおそらくあなたが TIME_ZONE で正しい値を設定することを忘れていたことを意味する。 これを変更して、ページをリロードし、正しい値が表示されるか確認。

「Today」や「Now」ショートカットをクリックして、「Date published」を変更してみる。 変更したら、「Save and Continue editing」を押下。 次に、右上に ある「履歴 (History)」をクリックしてみる。 ユーザが管理サイト上でオブジェクトに対して行った変更履歴の全てを、変更時刻と変更を行ったユーザ名付きでリストにしたページが表示される: